import type {
FormEncType,
HTMLFormMethod,
RelativeRoutingType,
} from "@remix-run/router";
import { stripBasename, UNSAFE_warning as warning } from "@remix-run/router";
export const defaultMethod: HTMLFormMethod = "get";
const defaultEncType: FormEncType = "application/x-www-form-urlencoded";
export function isHtmlElement(object: any): object is HTMLElement {
return object != null && typeof object.tagName === "string";
}
export function isButtonElement(object: any): object is HTMLButtonElement {
return isHtmlElement(object) && object.tagName.toLowerCase() === "button";
}
export function isFormElement(object: any): object is HTMLFormElement {
return isHtmlElement(object) && object.tagName.toLowerCase() === "form";
}
export function isInputElement(object: any): object is HTMLInputElement {
return isHtmlElement(object) && object.tagName.toLowerCase() === "input";
}
type LimitedMouseEvent = Pick<
MouseEvent,
"button" | "metaKey" | "altKey" | "ctrlKey" | "shiftKey"
>;
function isModifiedEvent(event: LimitedMouseEvent) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
export function shouldProcessLinkClick(
event: LimitedMouseEvent,
target?: string
) {
return (
event.button === 0 && // Ignore everything but left clicks
(!target || target === "_self") && // Let browser handle "target=_blank" etc.
!isModifiedEvent(event) // Ignore clicks with modifier keys
);
}
export type ParamKeyValuePair = [string, string];
export type URLSearchParamsInit =
| string
| ParamKeyValuePair[]
| Record
| URLSearchParams;
/**
* Creates a URLSearchParams object using the given initializer.
*
* This is identical to `new URLSearchParams(init)` except it also
* supports arrays as values in the object form of the initializer
* instead of just strings. This is convenient when you need multiple
* values for a given key, but don't want to use an array initializer.
*
* For example, instead of:
*
* let searchParams = new URLSearchParams([
* ['sort', 'name'],
* ['sort', 'price']
* ]);
*
* you can do:
*
* let searchParams = createSearchParams({
* sort: ['name', 'price']
* });
*/
export function createSearchParams(
init: URLSearchParamsInit = ""
): URLSearchParams {
return new URLSearchParams(
typeof init === "string" ||
Array.isArray(init) ||
init instanceof URLSearchParams
? init
: Object.keys(init).reduce((memo, key) => {
let value = init[key];
return memo.concat(
Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]]
);
}, [] as ParamKeyValuePair[])
);
}
export function getSearchParamsForLocation(
locationSearch: string,
defaultSearchParams: URLSearchParams | null
) {
let searchParams = createSearchParams(locationSearch);
if (defaultSearchParams) {
// Use `defaultSearchParams.forEach(...)` here instead of iterating of
// `defaultSearchParams.keys()` to work-around a bug in Firefox related to
// web extensions. Relevant Bugzilla tickets:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1414602
// https://bugzilla.mozilla.org/show_bug.cgi?id=1023984
defaultSearchParams.forEach((_, key) => {
if (!searchParams.has(key)) {
defaultSearchParams.getAll(key).forEach((value) => {
searchParams.append(key, value);
});
}
});
}
return searchParams;
}
// Thanks https://github.com/sindresorhus/type-fest!
type JsonObject = { [Key in string]: JsonValue } & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
export type SubmitTarget =
| HTMLFormElement
| HTMLButtonElement
| HTMLInputElement
| FormData
| URLSearchParams
| JsonValue
| null;
// One-time check for submitter support
let _formDataSupportsSubmitter: boolean | null = null;
function isFormDataSubmitterSupported() {
if (_formDataSupportsSubmitter === null) {
try {
new FormData(
document.createElement("form"),
// @ts-expect-error if FormData supports the submitter parameter, this will throw
0
);
_formDataSupportsSubmitter = false;
} catch (e) {
_formDataSupportsSubmitter = true;
}
}
return _formDataSupportsSubmitter;
}
export interface SubmitOptions {
/**
* The HTTP method used to submit the form. Overrides `