web: update to ESLint 9 (#10812)
* web: update to ESLint 9 ESLint 9 has been out for awhile now, and all of the plug-ins that we use have caught up, so it is time to bite the bullet and upgrade. This commit: - upgrades to ESLint 9, and upgrades all associated plugins - Replaces the `.eslintrc` and `.eslintignore` files with the new, "flat" configuration file, "eslint.config.mjs". - Places the previous "precommit" and "nightmare" rules in `./scripts/eslint.precommit.mjs` and `./scripts/eslint.nightmare.mjs`, respectively - Replaces the scripted wrappers for eslint (`eslint`, `eslint-precommit`) with a single executable that takes the arguments `--precommit`, which applies a stricter set of rules, and `--nightmare`, which applies an even more terrifyingly strict set of rules. - Provides the scripted wrapper `./scripts/eslint.mjs` so that eslint can be run from `bun`, if one so chooses. - Fixes *all* of the lint `eslint.config.mjs` now finds, including removing all of the `eslint` styling rules and overrides because Eslint now proudly leaves that entirely up to Prettier. To shut Dependabot up about ESLint. * Added explanation for no-console removal. * web: did not need the old and unmaintained nightmare mode; it can be configured directly.
This commit is contained in:
@ -48,7 +48,9 @@ function assignValue(element: HTMLNamedElement, value: unknown, json: KeyUnknown
|
||||
for (let index = 0; index < nameElements.length - 1; index++) {
|
||||
const nameEl = nameElements[index];
|
||||
// Ensure all nested structures exist
|
||||
if (!(nameEl in parent)) parent[nameEl] = {};
|
||||
if (!(nameEl in parent)) {
|
||||
parent[nameEl] = {};
|
||||
}
|
||||
parent = parent[nameEl] as { [key: string]: unknown };
|
||||
}
|
||||
parent[nameElements[nameElements.length - 1]] = value;
|
||||
@ -103,7 +105,7 @@ export function serializeForm<T extends KeyUnknown>(
|
||||
} else if (
|
||||
inputElement.tagName.toLowerCase() === "input" &&
|
||||
"type" in inputElement.dataset &&
|
||||
inputElement.dataset["type"] === "datetime-local"
|
||||
inputElement.dataset.type === "datetime-local"
|
||||
) {
|
||||
// Workaround for Firefox <93, since 92 and older don't support
|
||||
// datetime-local fields
|
||||
@ -122,6 +124,9 @@ export function serializeForm<T extends KeyUnknown>(
|
||||
return json as unknown as T;
|
||||
}
|
||||
|
||||
const HTTP_BAD_REQUEST = 400;
|
||||
const HTTP_INTERNAL_SERVICE_ERROR = 500;
|
||||
|
||||
/**
|
||||
* Form
|
||||
*
|
||||
@ -188,7 +193,7 @@ export abstract class Form<T> extends AKElement {
|
||||
*/
|
||||
get isInViewport(): boolean {
|
||||
const rect = this.getBoundingClientRect();
|
||||
return !(rect.x + rect.y + rect.width + rect.height === 0);
|
||||
return rect.x + rect.y + rect.width + rect.height !== 0;
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
@ -275,7 +280,6 @@ export abstract class Form<T> extends AKElement {
|
||||
}
|
||||
return serializeForm(elements) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize and send the form to the destination. The `send()` method must be overridden for
|
||||
* this to work. If processing the data results in an error, we catch the error, distribute
|
||||
@ -304,9 +308,14 @@ export abstract class Form<T> extends AKElement {
|
||||
} catch (ex) {
|
||||
if (ex instanceof ResponseError) {
|
||||
let msg = ex.response.statusText;
|
||||
if (ex.response.status > 399 && ex.response.status < 500) {
|
||||
if (
|
||||
ex.response.status >= HTTP_BAD_REQUEST &&
|
||||
ex.response.status < HTTP_INTERNAL_SERVICE_ERROR
|
||||
) {
|
||||
const errorMessage = ValidationErrorFromJSON(await ex.response.json());
|
||||
if (!errorMessage) return errorMessage;
|
||||
if (!errorMessage) {
|
||||
return errorMessage;
|
||||
}
|
||||
if (errorMessage instanceof Error) {
|
||||
throw errorMessage;
|
||||
}
|
||||
@ -318,7 +327,9 @@ export abstract class Form<T> extends AKElement {
|
||||
elements.forEach((element) => {
|
||||
element.requestUpdate();
|
||||
const elementName = element.name;
|
||||
if (!elementName) return;
|
||||
if (!elementName) {
|
||||
return;
|
||||
}
|
||||
if (camelToSnake(elementName) in errorMessage) {
|
||||
element.errorMessages = errorMessage[camelToSnake(elementName)];
|
||||
element.invalid = true;
|
||||
|
||||
@ -20,7 +20,8 @@ import {
|
||||
SearchSelectSelectEvent,
|
||||
SearchSelectSelectMenuEvent,
|
||||
} from "./SearchSelectEvents.js";
|
||||
import type { SearchOptions, SearchTuple } from "./types.js";
|
||||
import type { SearchOptions } from "./types.js";
|
||||
import { optionsToOptionsMap } from "./utils.js";
|
||||
|
||||
/**
|
||||
* @class SearchSelectView
|
||||
@ -225,8 +226,8 @@ export class SearchSelectView extends AKElement {
|
||||
}
|
||||
|
||||
updated() {
|
||||
if (!(this.inputRef?.value && this.inputRef?.value?.value === this.displayValue)) {
|
||||
this.inputRef.value && (this.inputRef.value.value = this.displayValue);
|
||||
if (this.inputRef?.value && this.inputRef?.value?.value !== this.displayValue) {
|
||||
this.inputRef.value.value = this.displayValue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,21 +265,6 @@ export class SearchSelectView extends AKElement {
|
||||
}
|
||||
}
|
||||
|
||||
type Pair = [string, string];
|
||||
const justThePair = ([key, label]: SearchTuple): Pair => [key, label];
|
||||
|
||||
function optionsToOptionsMap(options: SearchOptions): Map<string, string> {
|
||||
const pairs: Pair[] = Array.isArray(options)
|
||||
? options.map(justThePair)
|
||||
: options.grouped
|
||||
? options.options.reduce(
|
||||
(acc: Pair[], { options }): Pair[] => [...acc, ...options.map(justThePair)],
|
||||
[] as Pair[],
|
||||
)
|
||||
: options.options.map(justThePair);
|
||||
return new Map(pairs);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-search-select-view": SearchSelectView;
|
||||
|
||||
@ -97,11 +97,6 @@ export class SearchSelect<T> extends CustomEmitterElement(AkControlElement) {
|
||||
@state()
|
||||
error?: APIErrorTypes;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.dataset.akControl = "true";
|
||||
}
|
||||
|
||||
toForm(): unknown {
|
||||
if (!this.objects) {
|
||||
throw new PreventFormSubmit(msg("Loading options..."));
|
||||
@ -113,9 +108,9 @@ export class SearchSelect<T> extends CustomEmitterElement(AkControlElement) {
|
||||
return this.toForm();
|
||||
}
|
||||
|
||||
updateData() {
|
||||
async updateData() {
|
||||
if (this.isFetchingData) {
|
||||
return;
|
||||
return Promise.resolve();
|
||||
}
|
||||
this.isFetchingData = true;
|
||||
return this.fetchObjects(this.query)
|
||||
@ -140,6 +135,7 @@ export class SearchSelect<T> extends CustomEmitterElement(AkControlElement) {
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this.dataset.akControl = "true";
|
||||
this.updateData();
|
||||
this.addEventListener(EVENT_REFRESH, this.updateData);
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ const metadata: Meta<SearchSelectMenu> = {
|
||||
|
||||
export default metadata;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const onClick = (event: SearchSelectSelectMenuEvent) => {
|
||||
const target = document.querySelector("#action-button-message-pad");
|
||||
target!.innerHTML = "";
|
||||
|
||||
16
web/src/elements/forms/SearchSelect/utils.ts
Normal file
16
web/src/elements/forms/SearchSelect/utils.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import type { SearchOptions, SearchTuple } from "./types.js";
|
||||
|
||||
type Pair = [string, string];
|
||||
const justThePair = ([key, label]: SearchTuple): Pair => [key, label];
|
||||
|
||||
export function optionsToOptionsMap(options: SearchOptions): Map<string, string> {
|
||||
const pairs: Pair[] = Array.isArray(options)
|
||||
? options.map(justThePair)
|
||||
: options.grouped
|
||||
? options.options.reduce(
|
||||
(acc: Pair[], { options }): Pair[] => [...acc, ...options.map(justThePair)],
|
||||
[] as Pair[],
|
||||
)
|
||||
: options.options.map(justThePair);
|
||||
return new Map(pairs);
|
||||
}
|
||||
Reference in New Issue
Block a user