From 1f075c3247ab45c0e6f1fc78f013ad2bcbd21aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 14:46:01 +0100 Subject: [PATCH 01/17] Add types for `jquery.lengthLimit` --- jquery/lengthLimit.d.ts | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 jquery/lengthLimit.d.ts diff --git a/jquery/lengthLimit.d.ts b/jquery/lengthLimit.d.ts new file mode 100644 index 0000000..b91cb1d --- /dev/null +++ b/jquery/lengthLimit.d.ts @@ -0,0 +1,78 @@ +declare global { + interface JQueryStatic { + /** + * Utility function to trim down a string, based on byteLimit + * and given a safe start position. It supports insertion anywhere + * in the string, so "foo" to "fobaro" if limit is 4 will result in + * "fobo", not "foba". Basically emulating the native maxlength by + * reconstructing where the insertion occurred. + * + * @deprecated Use `require( 'mediawiki.String' ).trimByteLength` instead. + * @param {string} safeVal Known value that was previously returned by this + * function, if none, pass empty string. + * @param {string} newVal New value that may have to be trimmed down. + * @param {number} byteLimit Number of bytes the value may be in size. + * @param {FilterFunction} [filterFunction] Function to call on the string before assessing the length. + * @returns {TrimResult} + */ + trimByteLength( + safeVal: string, + newVal: string, + byteLimit: number, + filterFunction?: FilterFunction + ): TrimResult; + } + + interface JQuery { + /** + * Enforces a byte limit on an input field, assuming UTF-8 encoding, for situations + * when, for example, a database field has a byte limit rather than a character limit. + * Plugin rationale: Browser has native maxlength for number of characters (technically, + * UTF-16 code units), this plugin exists to limit number of bytes instead. + * + * Can be called with a custom limit (to use that limit instead of the maxlength attribute + * value), a filter function (in case the limit should apply to something other than the + * exact input value), or both. Order of parameters is important! + * + * @param {number} [limit] Limit to enforce, fallsback to maxLength-attribute, + * called with fetched value as argument. + * @param {FilterFunction} [filterFunction] Function to call on the string before assessing the length. + * @returns {JQuery} + */ + byteLimit(limit: number, filterFunction?: FilterFunction): this; + byteLimit(filterFunction?: FilterFunction): this; + + /** + * Enforces a codepoint (character) limit on an input field. + * + * For unfortunate historical reasons, browsers' native maxlength counts [the number of UTF-16 + * code units rather than Unicode codepoints] [1], which means that codepoints outside the Basic + * Multilingual Plane (e.g. many emojis) count as 2 characters each. This plugin exists to + * correct this. + * + * [1]: https://www.w3.org/TR/html5/sec-forms.html#limiting-user-input-length-the-maxlength-attribute + * + * Can be called with a custom limit (to use that limit instead of the maxlength attribute + * value), a filter function (in case the limit should apply to something other than the + * exact input value), or both. Order of parameters is important! + * + * @param {number} [limit] Limit to enforce, fallsback to maxLength-attribute, + * called with fetched value as argument. + * @param {FilterFunction} [filterFunction] Function to call on the string before assessing the length. + * @returns {JQuery} + */ + codePointLimit(limit: number, filterFunction?: FilterFunction): this; + codePointLimit(filterFunction?: FilterFunction): this; + } +} + +interface FilterFunction { + (str: string): string; +} + +interface TrimResult { + newVal: string; + trimmed: boolean; +} + +export {}; From 6f818b6fdb76090c3f57cfddda04d245e8f08dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 15:14:40 +0100 Subject: [PATCH 02/17] Add types for `jquery.makeCollapsible` --- jquery/makeCollapsible.d.ts | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 jquery/makeCollapsible.d.ts diff --git a/jquery/makeCollapsible.d.ts b/jquery/makeCollapsible.d.ts new file mode 100644 index 0000000..0041aad --- /dev/null +++ b/jquery/makeCollapsible.d.ts @@ -0,0 +1,53 @@ +declare global { + interface JQuery { + /** + * Enable collapsible-functionality on all elements in the collection. + * + * - Will prevent binding twice to the same element. + * - Initial state is expanded by default, this can be overridden by adding class + * "mw-collapsed" to the "mw-collapsible" element. + * - Elements made collapsible have jQuery data "mw-made-collapsible" set to true. + * - The inner content is wrapped in a "div.mw-collapsible-content" (except for tables and lists). + * + * @param {Options} [options] + * @return {JQuery} + */ + makeCollapsible(options?: Options): this; + } +} + +interface Options { + /** + * Elements to be used as togglers for this collapsible element. By default, if the collapsible + * element has an id attribute like 'mw-customcollapsible-XXX', elements with a **class** + * of 'mw-customtoggle-XXX' are made togglers for it. + */ + $customTogglers?: JQuery; + + /** + * Whether to collapse immediately. By default collapse only if the element has the 'mw-collapsed' class. + */ + collapsed?: boolean; + + /** + * Text used for the toggler, when clicking it would collapse the element. + * Default: the 'data-collapsetext' attribute of the collapsible element or the content of 'collapsible-collapse' message. + */ + collapseText?: string; + + /** + * Text used for the toggler, when clicking it would expand the element. + * Default: the 'data-expandtext' attribute of the collapsible element or the content of 'collapsible-expand' message. + */ + expandText?: string; + + /** + * Whether to use a "plain mode" when making the element collapsible - that is, hide entire tables + * and lists (instead of hiding only all rows but first of tables, and hiding each list item + * separately for lists) and don't wrap other elements in div.mw-collapsible-content. + * May only be used with custom togglers. + */ + plainMode?: boolean; +} + +export {}; From 6fe67cc0019326dbdbf73f51030452c6f1c8db0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 15:34:27 +0100 Subject: [PATCH 03/17] Add types for `jquery.spinner` --- jquery/makeCollapsible.d.ts | 2 +- jquery/spinner.d.ts | 85 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 jquery/spinner.d.ts diff --git a/jquery/makeCollapsible.d.ts b/jquery/makeCollapsible.d.ts index 0041aad..e13b597 100644 --- a/jquery/makeCollapsible.d.ts +++ b/jquery/makeCollapsible.d.ts @@ -10,7 +10,7 @@ declare global { * - The inner content is wrapped in a "div.mw-collapsible-content" (except for tables and lists). * * @param {Options} [options] - * @return {JQuery} + * @returns {JQuery} */ makeCollapsible(options?: Options): this; } diff --git a/jquery/spinner.d.ts b/jquery/spinner.d.ts new file mode 100644 index 0000000..c79dbda --- /dev/null +++ b/jquery/spinner.d.ts @@ -0,0 +1,85 @@ +declare global { + interface JQueryStatic { + /** + * Create a spinner element + * + * The argument is an object with options used to construct the spinner (see below). + * + * It is a good practice to keep a reference to the created spinner to be able to remove it + * later. Alternatively, one can use the 'id' option and #removeSpinner (but make sure to choose + * an id that's unlikely to cause conflicts, e.g. with extensions, gadgets or user scripts). + * + * CSS classes used: + * + * - .mw-spinner for every spinner + * - .mw-spinner-small / .mw-spinner-large for size + * - .mw-spinner-block / .mw-spinner-inline for display types + * + * @example + * ```js + * // Create a large spinner reserving all available horizontal space. + * var $spinner = $.createSpinner( { size: 'large', type: 'block' } ); + * // Insert above page content. + * $( '#mw-content-text' ).prepend( $spinner ); + * + * // Place a small inline spinner next to the "Save" button + * var $spinner = $.createSpinner( { size: 'small', type: 'inline' } ); + * // Alternatively, just `$.createSpinner();` as these are the default options. + * $( '#wpSave' ).after( $spinner ); + * + * // The following two are equivalent: + * $.createSpinner( 'magic' ); + * $.createSpinner( { id: 'magic' } ); + * ``` + * + * @param {string|Options} [opts] Options. If a string is given, it will be treated as the value + * of the `id` option. + * @returns {JQuery} + */ + createSpinner(opts?: string | Options): JQuery; + + /** + * Remove a spinner element. + * + * @param {string} id Id of the spinner, as passed to {@link createSpinner} + * @returns {JQuery} The (now detached) spinner element + */ + removeSpinner(id: string): JQuery; + } + + interface JQuery { + /** + * Inject a spinner after each element in the collection + * + * Inserts spinner as siblings (not children) of the target elements. + * Collection contents remain unchanged. + * + * @param {string|Object} [opts] Options. If a string is given, it will be treated as the value + * of the `id` option. + * @returns {JQuery} + */ + injectSpinner(opts?: string | Options): this; + } +} + +type Size = "large" | "small"; +type Type = "block" | "inline"; + +interface Options { + /** + * If given, spinner will be given an id of "mw-spinner-{id}". + */ + id?: string | undefined; + + /** + * 'small' or 'large' for a 20-pixel or 32-pixel spinner. + */ + size?: Size; + + /** + * 'inline' or 'block'. Inline creates an inline-block with width and height equal to spinner size. Block is a block-level element with width 100%, height equal to spinner size. + */ + type?: Type; +} + +export {}; From dfaaa9906915720185de95cd914a0c9384ae6c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 15:46:59 +0100 Subject: [PATCH 04/17] Fix `no-redundant-undefined` This rule was disabled in dtslint 4.1.1, so updating it is enough --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index dd23cd4..a9df7a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@types/oojs-ui": "^0.46.0" }, "devDependencies": { - "dtslint": "^4.0.6", + "dtslint": "^4.1.1", "husky": "^4.3.7", "lint-staged": "^10.5.3", "prettier": "^2.2.1", @@ -895,9 +895,9 @@ } }, "node_modules/dtslint": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-4.0.8.tgz", - "integrity": "sha512-ypMFCfHwWmbELC1xXS3dJ+tPt/iAc6emWpIdavtV7ubsmzc9wVd2joa7RS/3yQ9g9EPBwylIWMSMf/bItS1Dbw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-4.1.1.tgz", + "integrity": "sha512-8SD3My7Cf1OnldIBFIwOPCaIg9PqgRumX0Xf/bbcOop5VG66iwok787DiuoJVhlvInoP5uFr8cBKGFNtwi25Ww==", "dev": true, "dependencies": { "@definitelytyped/header-parser": "latest", @@ -4109,9 +4109,9 @@ } }, "dtslint": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-4.0.8.tgz", - "integrity": "sha512-ypMFCfHwWmbELC1xXS3dJ+tPt/iAc6emWpIdavtV7ubsmzc9wVd2joa7RS/3yQ9g9EPBwylIWMSMf/bItS1Dbw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/dtslint/-/dtslint-4.1.1.tgz", + "integrity": "sha512-8SD3My7Cf1OnldIBFIwOPCaIg9PqgRumX0Xf/bbcOop5VG66iwok787DiuoJVhlvInoP5uFr8cBKGFNtwi25Ww==", "dev": true, "requires": { "@definitelytyped/header-parser": "latest", diff --git a/package.json b/package.json index 39a67a4..cff84e5 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@types/oojs-ui": "^0.46.0" }, "devDependencies": { - "dtslint": "^4.0.6", + "dtslint": "^4.1.1", "husky": "^4.3.7", "lint-staged": "^10.5.3", "prettier": "^2.2.1", From afd2530215d496ac4a2dbf7ec3dddcae04615294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 16:03:56 +0100 Subject: [PATCH 05/17] Add types for `jquery.suggestions` Use an additional type variable `T` to specify metadata type --- jquery/suggestions.d.ts | 168 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 jquery/suggestions.d.ts diff --git a/jquery/suggestions.d.ts b/jquery/suggestions.d.ts new file mode 100644 index 0000000..99cefe0 --- /dev/null +++ b/jquery/suggestions.d.ts @@ -0,0 +1,168 @@ +declare global { + interface JQuery { + /** + * This plugin provides a generic way to add suggestions to a text box. + * + * Set options: + * + * ```js + * $( '#textbox' ).suggestions( { option1: value1, option2: value2 } ); + * $( '#textbox' ).suggestions( option, value ); + * ``` + * + * Initialize: + * + * ```js + * $( '#textbox' ).suggestions(); + * ``` + * + * @param {string} property Name of property + * @param {any} value Value to set property with + * @returns {JQuery} + */ + suggestions, T = any>(property: K, value: Options[K]): this; + suggestions(options?: Partial>): this; + } +} + +type Device = "keyboard" | "mouse"; +type Direction = "auto" | "end" | "left" | "right" | "start"; + +interface Context { + config: Options; + data: unknown; +} + +interface Options { + /** + * Whether to cache results from a fetch. Defaults to false. + */ + cache: boolean; + + /** + * Number of milliseconds to cache results from a fetch. Must be higher than 1. Defaults to 1 minute. + */ + cacheMaxAge: number; + + /** + * Callback function to call when any pending asynchronous suggestions fetches. Called in context of the text box. + */ + cancel(this: JQuery): void; + + /** + * Number of milliseconds to wait for the user to stop typing. Must be between 0 and 1200. Defaults to 120. + */ + delay: number; + + /** + * Which direction to offset the suggestion box from. + * Values 'start' and 'end' translate to left and right respectively depending on the directionality + * of the current document, according to `$( document.documentElement ).css( 'direction' )`. + * Valid values: "left", "right", "start", "end", and "auto". Defaults to auto. + */ + expandFrom: Direction; + + /** + * Callback that should fetch suggestions and set the suggestions property. Called in context of the text box. + * + * @param {string} query + * @param {function(string[],any):void} response Callback to receive the suggestions with + * @param {number} maxRows + */ + fetch( + this: JQuery, + query: string, + response: (suggestions: string[], metadata: T) => void, + maxRows: number + ): void; + + /** + * Whether to highlight matched portions of the input or not. Defaults to false. + */ + highlightInput: boolean; + + /** + * Maximum suggestions box width relative to the textbox width. + * If set to e.g. 2, the suggestions box will never be grown beyond 2 times the width of the textbox. + * Must be higher than 1. Defaults to 3. + */ + maxExpandFactor: number; + + /** + * Maximum number of suggestions to display at one time. Must be between 1 and 100. + */ + maxRows: number; + + /** + * Sets `expandFrom=left`, for backwards compatibility. + */ + positionFromLeft: boolean; + + /** + * Set of callbacks for rendering and selecting. + */ + result: Partial>; + + /** + * Set of callbacks for rendering and selecting. + */ + special: Partial>; + + /** + * Whether to submit the form containing the textbox when a suggestion is clicked. Defaults to false. + */ + submitOnClick: boolean; + + /** + * Array of suggestions to display. + */ + suggestions: string[]; + + /** + * Set of callbacks for listening to a change in the text input. + */ + update: Partial>; + + /** + * The element to place the suggestions below and match width of. Defaults to the element itself. + */ + $region: JQuery; +} + +interface ResultOptions { + /** + * Called in context of the suggestions-result element. + */ + render(this: JQuery, suggestion: string, context: Context): void; + + /** + * Called in context of the suggestions-result-current element. + */ + select(this: JQuery, $textbox: JQuery, device: Device): any; +} + +interface SpecialOptions { + /** + * Called in context of the suggestions-special element. + */ + render(this: JQuery, query: string, context: Context): void; + + /** + * Called in context of the suggestions-result-current element. + */ + select(this: JQuery, $textbox: JQuery, device: Device): boolean; +} + +interface UpdateOptions { + /** + * Called after results are updated either from the cache or the API as a result of the user input. + */ + after(this: JQuery, metadata: T): void; + + /** + * Called right after the user changes the textbox text. + */ + before(this: JQuery): void; +} + +export {}; From 9aa88e80d22019f0d31318ea79f190b5bda89c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 8 Mar 2024 16:12:17 +0100 Subject: [PATCH 06/17] Add types for `jquery.confirmable` --- jquery/confirmable.d.ts | 100 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 jquery/confirmable.d.ts diff --git a/jquery/confirmable.d.ts b/jquery/confirmable.d.ts new file mode 100644 index 0000000..e87bcee --- /dev/null +++ b/jquery/confirmable.d.ts @@ -0,0 +1,100 @@ +declare global { + interface JQuery { + confirmable: Confirmable; + } +} + +interface Confirmable { + /** + * Enable inline confirmation for given clickable element (like `` or `