update BiomeJS

This commit is contained in:
imperosol
2026-02-13 14:07:46 +01:00
parent e71f76ea91
commit 84033f37cf
41 changed files with 124 additions and 95 deletions

View File

@@ -12,7 +12,7 @@ repos:
rev: v0.6.1 rev: v0.6.1
hooks: hooks:
- id: biome-check - id: biome-check
additional_dependencies: ["@biomejs/biome@1.9.4"] additional_dependencies: ["@biomejs/biome@2.3.14"]
- repo: https://github.com/rtts/djhtml - repo: https://github.com/rtts/djhtml
rev: 3.0.10 rev: 3.0.10
hooks: hooks:

View File

@@ -7,20 +7,34 @@
}, },
"files": { "files": {
"ignoreUnknown": false, "ignoreUnknown": false,
"ignore": ["*.min.*", "staticfiles/generated"] "includes": ["**/static/**"]
}, },
"formatter": { "formatter": {
"enabled": true, "enabled": true,
"indentStyle": "space", "indentStyle": "space",
"lineWidth": 88 "lineWidth": 88
}, },
"organizeImports": {
"enabled": true
},
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"all": true "recommended": true,
"style": {
"useNamingConvention": "error"
},
"performance": {
"noNamespaceImport": "error"
},
"suspicious": {
"noConsole": {
"level": "error",
"options": { "allow": ["error", "warn"] }
}
},
"correctness": {
"noUnusedVariables": "error",
"noUndeclaredVariables": "error",
"noUndeclaredDependencies": "error"
}
} }
}, },
"javascript": { "javascript": {

View File

@@ -1,7 +1,7 @@
import { AjaxSelect } from "#core:core/components/ajax-select-base";
import { registerComponent } from "#core:utils/web-components";
import type { TomOption } from "tom-select/dist/types/types"; import type { TomOption } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils"; import type { escape_html } from "tom-select/dist/types/utils";
import { AjaxSelect } from "#core:core/components/ajax-select-base.ts";
import { registerComponent } from "#core:utils/web-components.ts";
import { type ClubSchema, clubSearchClub } from "#openapi"; import { type ClubSchema, clubSearchClub } from "#openapi";
@registerComponent("club-ajax-select") @registerComponent("club-ajax-select")

View File

@@ -1,5 +1,3 @@
import { makeUrl } from "#core:utils/api";
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
import { Calendar, type EventClickArg, type EventContentArg } from "@fullcalendar/core"; import { Calendar, type EventClickArg, type EventContentArg } from "@fullcalendar/core";
import type { EventImpl } from "@fullcalendar/core/internal"; import type { EventImpl } from "@fullcalendar/core/internal";
import enLocale from "@fullcalendar/core/locales/en-gb"; import enLocale from "@fullcalendar/core/locales/en-gb";
@@ -8,6 +6,8 @@ import dayGridPlugin from "@fullcalendar/daygrid";
import iCalendarPlugin from "@fullcalendar/icalendar"; import iCalendarPlugin from "@fullcalendar/icalendar";
import listPlugin from "@fullcalendar/list"; import listPlugin from "@fullcalendar/list";
import { type HTMLTemplateResult, html, render } from "lit-html"; import { type HTMLTemplateResult, html, render } from "lit-html";
import { makeUrl } from "#core:utils/api.ts";
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
import { import {
calendarCalendarInternal, calendarCalendarInternal,
calendarCalendarUnpublished, calendarCalendarUnpublished,
@@ -95,6 +95,7 @@ export class IcsCalendar extends inheritHtmlElement("div") {
.split("/") .split("/")
.filter((s) => s) // Remove blank characters .filter((s) => s) // Remove blank characters
.pop(), .pop(),
10,
); );
} }

View File

@@ -1,4 +1,4 @@
import { exportToHtml } from "#core:utils/globals"; import { exportToHtml } from "#core:utils/globals.ts";
import { newsDeleteNews, newsFetchNewsDates, newsPublishNews } from "#openapi"; import { newsDeleteNews, newsFetchNewsDates, newsPublishNews } from "#openapi";
// This will be used in jinja templates, // This will be used in jinja templates,

View File

@@ -1,7 +1,7 @@
import { limitedChoices } from "#core:alpine/limited-choices";
import { alpinePlugin as notificationPlugin } from "#core:utils/notifications";
import sort from "@alpinejs/sort"; import sort from "@alpinejs/sort";
import Alpine from "alpinejs"; import Alpine from "alpinejs";
import { limitedChoices } from "#core:alpine/limited-choices.ts";
import { alpinePlugin as notificationPlugin } from "#core:utils/notifications.ts";
Alpine.plugin([sort, limitedChoices]); Alpine.plugin([sort, limitedChoices]);
Alpine.magic("notifications", notificationPlugin); Alpine.magic("notifications", notificationPlugin);

View File

@@ -56,7 +56,7 @@ export function limitedChoices(Alpine: AlpineType) {
effect(() => { effect(() => {
getMaxChoices((value: string) => { getMaxChoices((value: string) => {
const previousValue = maxChoices; const previousValue = maxChoices;
maxChoices = Number.parseInt(value); maxChoices = Number.parseInt(value, 10);
if (maxChoices < previousValue) { if (maxChoices < previousValue) {
// The maximum number of selectable items has been lowered. // The maximum number of selectable items has been lowered.
// Some currently selected elements may need to be removed // Some currently selected elements may need to be removed

View File

@@ -1,4 +1,3 @@
import { inheritHtmlElement } from "#core:utils/web-components";
import TomSelect from "tom-select"; import TomSelect from "tom-select";
import type { import type {
RecursivePartial, RecursivePartial,
@@ -7,6 +6,7 @@ import type {
TomSettings, TomSettings,
} from "tom-select/dist/types/types"; } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils"; import type { escape_html } from "tom-select/dist/types/utils";
import { inheritHtmlElement } from "#core:utils/web-components.ts";
export class AutoCompleteSelectBase extends inheritHtmlElement("select") { export class AutoCompleteSelectBase extends inheritHtmlElement("select") {
static observedAttributes = [ static observedAttributes = [
@@ -29,7 +29,7 @@ export class AutoCompleteSelectBase extends inheritHtmlElement("select") {
) { ) {
switch (name) { switch (name) {
case "delay": { case "delay": {
this.delay = Number.parseInt(newValue) ?? null; this.delay = Number.parseInt(newValue, 10) ?? null;
break; break;
} }
case "placeholder": { case "placeholder": {
@@ -37,11 +37,11 @@ export class AutoCompleteSelectBase extends inheritHtmlElement("select") {
break; break;
} }
case "max": { case "max": {
this.max = Number.parseInt(newValue) ?? null; this.max = Number.parseInt(newValue, 10) ?? null;
break; break;
} }
case "min-characters-for-search": { case "min-characters-for-search": {
this.minCharNumberForSearch = Number.parseInt(newValue) ?? 0; this.minCharNumberForSearch = Number.parseInt(newValue, 10) ?? 0;
break; break;
} }
default: { default: {

View File

@@ -1,20 +1,19 @@
import "tom-select/dist/css/tom-select.default.css"; import "tom-select/dist/css/tom-select.default.css";
import { registerComponent } from "#core:utils/web-components";
import type { TomOption } from "tom-select/dist/types/types"; import type { TomOption } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils"; import type { escape_html } from "tom-select/dist/types/utils";
import {
type GroupSchema,
type SithFileSchema,
type UserProfileSchema,
groupSearchGroup,
sithfileSearchFiles,
userSearchUsers,
} from "#openapi";
import { import {
AjaxSelect, AjaxSelect,
AutoCompleteSelectBase, AutoCompleteSelectBase,
} from "#core:core/components/ajax-select-base"; } from "#core:core/components/ajax-select-base.ts";
import { registerComponent } from "#core:utils/web-components.ts";
import {
type GroupSchema,
groupSearchGroup,
type SithFileSchema,
sithfileSearchFiles,
type UserProfileSchema,
userSearchUsers,
} from "#openapi";
@registerComponent("autocomplete-select") @registerComponent("autocomplete-select")
export class AutoCompleteSelect extends AutoCompleteSelectBase {} export class AutoCompleteSelect extends AutoCompleteSelectBase {}

View File

@@ -1,14 +1,14 @@
// biome-ignore lint/correctness/noUndeclaredDependencies: shipped by easymde // biome-ignore lint/correctness/noUndeclaredDependencies: shipped by easymde
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import "easymde/src/css/easymde.css"; import "easymde/src/css/easymde.css";
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components";
// biome-ignore lint/correctness/noUndeclaredDependencies: Imported by EasyMDE // biome-ignore lint/correctness/noUndeclaredDependencies: Imported by EasyMDE
import type CodeMirror from "codemirror"; import type CodeMirror from "codemirror";
// biome-ignore lint/style/useNamingConvention: This is how they called their namespace // biome-ignore lint/style/useNamingConvention: This is how they called their namespace
import EasyMDE from "easymde"; import EasyMDE from "easymde";
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
import { import {
type UploadUploadImageErrors,
markdownRenderMarkdown, markdownRenderMarkdown,
type UploadUploadImageErrors,
uploadUploadImage, uploadUploadImage,
} from "#openapi"; } from "#openapi";

View File

@@ -1,4 +1,4 @@
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components"; import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
/** /**
* Web component used to import css files only once * Web component used to import css files only once

View File

@@ -1,4 +1,4 @@
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components"; import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
@registerComponent("nfc-input") @registerComponent("nfc-input")
export class NfcInput extends inheritHtmlElement("input") { export class NfcInput extends inheritHtmlElement("input") {

View File

@@ -1,6 +1,6 @@
import { registerComponent } from "#core:utils/web-components";
import { html, render } from "lit-html"; import { html, render } from "lit-html";
import { unsafeHTML } from "lit-html/directives/unsafe-html.js"; import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
import { registerComponent } from "#core:utils/web-components.ts";
@registerComponent("ui-tab") @registerComponent("ui-tab")
export class Tab extends HTMLElement { export class Tab extends HTMLElement {

View File

@@ -1,4 +1,4 @@
import { exportToHtml } from "#core:utils/globals"; import { exportToHtml } from "#core:utils/globals.ts";
exportToHtml("showMenu", () => { exportToHtml("showMenu", () => {
const navbar = document.getElementById("navbar-content"); const navbar = document.getElementById("navbar-content");

View File

@@ -26,7 +26,7 @@ function showMore(element: HTMLElement) {
const fullContent = element.innerHTML; const fullContent = element.innerHTML;
const clippedContent = clip( const clippedContent = clip(
element.innerHTML, element.innerHTML,
Number.parseInt(element.getAttribute("show-more") as string), Number.parseInt(element.getAttribute("show-more") as string, 10),
{ {
html: true, html: true,
}, },

View File

@@ -1,9 +1,9 @@
import { import {
type Placement,
autoPlacement, autoPlacement,
computePosition, computePosition,
flip, flip,
offset, offset,
type Placement,
size, size,
} from "@floating-ui/dom"; } from "@floating-ui/dom";

View File

@@ -1,6 +1,6 @@
import { exportToHtml } from "#core:utils/globals"; // biome-ignore lint/performance/noNamespaceImport: this is the recommended way from the documentation
// biome-ignore lint/style/noNamespaceImport: this is the recommended way from the documentation
import * as Sentry from "@sentry/browser"; import * as Sentry from "@sentry/browser";
import { exportToHtml } from "#core:utils/globals.ts";
interface LoggedUser { interface LoggedUser {
name: string; name: string;

View File

@@ -1,4 +1,3 @@
import { History, initialUrlParams, updateQueryString } from "#core:utils/history";
import cytoscape, { import cytoscape, {
type ElementDefinition, type ElementDefinition,
type NodeSingular, type NodeSingular,
@@ -6,7 +5,8 @@ import cytoscape, {
} from "cytoscape"; } from "cytoscape";
import cxtmenu from "cytoscape-cxtmenu"; import cxtmenu from "cytoscape-cxtmenu";
import klay, { type KlayLayoutOptions } from "cytoscape-klay"; import klay, { type KlayLayoutOptions } from "cytoscape-klay";
import { type UserProfileSchema, familyGetFamilyGraph } from "#openapi"; import { History, initialUrlParams, updateQueryString } from "#core:utils/history.ts";
import { familyGetFamilyGraph, type UserProfileSchema } from "#openapi";
cytoscape.use(klay); cytoscape.use(klay);
cytoscape.use(cxtmenu); cytoscape.use(cxtmenu);
@@ -200,7 +200,7 @@ document.addEventListener("alpine:init", () => {
isZoomEnabled: !isMobile(), isZoomEnabled: !isMobile(),
getInitialDepth(prop: string) { getInitialDepth(prop: string) {
const value = Number.parseInt(initialUrlParams.get(prop)); const value = Number.parseInt(initialUrlParams.get(prop), 10);
if (Number.isNaN(value) || value < config.depthMin || value > config.depthMax) { if (Number.isNaN(value) || value < config.depthMin || value > config.depthMax) {
return defaultDepth; return defaultDepth;
} }

View File

@@ -1,5 +1,5 @@
import { client, type Options } from "#openapi";
import type { Client, RequestResult, TDataShape } from "#openapi:client"; import type { Client, RequestResult, TDataShape } from "#openapi:client";
import { type Options, client } from "#openapi";
export interface PaginatedResponse<T> { export interface PaginatedResponse<T> {
count: number; count: number;

View File

@@ -1,4 +1,4 @@
import type { NestedKeyOf } from "#core:utils/types"; import type { NestedKeyOf } from "#core:utils/types.ts";
interface StringifyOptions<T extends object> { interface StringifyOptions<T extends object> {
/** The columns to include in the resulting CSV. */ /** The columns to include in the resulting CSV. */

View File

@@ -10,7 +10,6 @@ export function registerComponent(name: string, options?: ElementDefinitionOptio
window.customElements.define(name, component, options); window.customElements.define(name, component, options);
} catch (e) { } catch (e) {
if (e instanceof DOMException) { if (e instanceof DOMException) {
// biome-ignore lint/suspicious/noConsole: it's handy to troobleshot
console.warn(e.message); console.warn(e.message);
return; return;
} }

View File

@@ -1,4 +1,4 @@
import type { Product } from "#counter:counter/types"; import type { Product } from "#counter:counter/types.ts";
export class BasketItem { export class BasketItem {
quantity: number; quantity: number;

View File

@@ -1,14 +1,14 @@
import { AjaxSelect } from "#core:core/components/ajax-select-base";
import { registerComponent } from "#core:utils/web-components";
import type { TomOption } from "tom-select/dist/types/types"; import type { TomOption } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils"; import type { escape_html } from "tom-select/dist/types/utils";
import { AjaxSelect } from "#core:core/components/ajax-select-base.ts";
import { registerComponent } from "#core:utils/web-components.ts";
import { import {
type CounterSchema, type CounterSchema,
type ProductTypeSchema,
type SimpleProductSchema,
counterSearchCounter, counterSearchCounter,
type ProductTypeSchema,
productSearchProducts, productSearchProducts,
producttypeFetchAll, producttypeFetchAll,
type SimpleProductSchema,
} from "#openapi"; } from "#openapi";
@registerComponent("product-ajax-select") @registerComponent("product-ajax-select")

View File

@@ -1,13 +1,13 @@
import { AutoCompleteSelectBase } from "#core:core/components/ajax-select-base";
import { registerComponent } from "#core:utils/web-components";
import type { RecursivePartial, TomSettings } from "tom-select/dist/types/types"; import type { RecursivePartial, TomSettings } from "tom-select/dist/types/types";
import { AutoCompleteSelectBase } from "#core:core/components/ajax-select-base.ts";
import { registerComponent } from "#core:utils/web-components.ts";
const productParsingRegex = /^(\d+x)?(.*)/i; const productParsingRegex = /^(\d+x)?(.*)/i;
const codeParsingRegex = / \((\w+)\)$/; const codeParsingRegex = / \((\w+)\)$/;
function parseProduct(query: string): [number, string] { function parseProduct(query: string): [number, string] {
const parsed = productParsingRegex.exec(query); const parsed = productParsingRegex.exec(query);
return [Number.parseInt(parsed[1] || "1"), parsed[2]]; return [Number.parseInt(parsed[1] || "1", 10), parsed[2]];
} }
@registerComponent("counter-product-select") @registerComponent("counter-product-select")
@@ -80,9 +80,9 @@ export class CounterProductSelect extends AutoCompleteSelectBase {
// We need to manually set weights or it results on an inconsistent // We need to manually set weights or it results on an inconsistent
// behavior between production and development environment // behavior between production and development environment
searchField: [ searchField: [
// @ts-ignore documentation says it's fine, specified type is wrong // @ts-expect-error documentation says it's fine, specified type is wrong
{ field: "code", weight: 2 }, { field: "code", weight: 2 },
// @ts-ignore documentation says it's fine, specified type is wrong // @ts-expect-error documentation says it's fine, specified type is wrong
{ field: "text", weight: 0.5 }, { field: "text", weight: 0.5 },
], ],
}; };

View File

@@ -1,6 +1,6 @@
import { AlertMessage } from "#core:utils/alert-message"; import { AlertMessage } from "#core:utils/alert-message.ts";
import { BasketItem } from "#counter:counter/basket"; import { BasketItem } from "#counter:counter/basket.ts";
import type { CounterConfig, ErrorMessage } from "#counter:counter/types"; import type { CounterConfig, ErrorMessage } from "#counter:counter/types.ts";
import type { CounterProductSelect } from "./components/counter-product-select-index.ts"; import type { CounterProductSelect } from "./components/counter-product-select-index.ts";
document.addEventListener("alpine:init", () => { document.addEventListener("alpine:init", () => {

View File

@@ -1,9 +1,13 @@
import { paginated } from "#core:utils/api";
import { csv } from "#core:utils/csv";
import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history";
import type { NestedKeyOf } from "#core:utils/types";
import { showSaveFilePicker } from "native-file-system-adapter"; import { showSaveFilePicker } from "native-file-system-adapter";
import type TomSelect from "tom-select"; import type TomSelect from "tom-select";
import { paginated } from "#core:utils/api.ts";
import { csv } from "#core:utils/csv.ts";
import {
getCurrentUrlParams,
History,
updateQueryString,
} from "#core:utils/history.ts";
import type { NestedKeyOf } from "#core:utils/types.ts";
import { import {
type ProductSchema, type ProductSchema,
type ProductSearchProductsDetailedData, type ProductSearchProductsDetailedData,

View File

@@ -1,5 +1,5 @@
import { AlertMessage } from "#core:utils/alert-message";
import Alpine from "alpinejs"; import Alpine from "alpinejs";
import { AlertMessage } from "#core:utils/alert-message.ts";
import { producttypeReorder } from "#openapi"; import { producttypeReorder } from "#openapi";
document.addEventListener("alpine:init", () => { document.addEventListener("alpine:init", () => {
@@ -22,7 +22,7 @@ document.addEventListener("alpine:init", () => {
const productTypes = this.$refs.productTypes const productTypes = this.$refs.productTypes
.childNodes as NodeListOf<HTMLLIElement>; .childNodes as NodeListOf<HTMLLIElement>;
const getId = (elem: HTMLLIElement) => const getId = (elem: HTMLLIElement) =>
Number.parseInt(elem.getAttribute("x-sort:item")); Number.parseInt(elem.getAttribute("x-sort:item"), 10);
const query = const query =
newPosition === 0 newPosition === 0
? { above: getId(productTypes.item(1)) } ? { above: getId(productTypes.item(1)) }

View File

@@ -21,7 +21,7 @@ document.addEventListener("alpine:init", () => {
if (lastPurchaseTime !== null && localStorage.basketTimestamp !== undefined) { if (lastPurchaseTime !== null && localStorage.basketTimestamp !== undefined) {
if ( if (
new Date(lastPurchaseTime) >= new Date(lastPurchaseTime) >=
new Date(Number.parseInt(localStorage.basketTimestamp)) new Date(Number.parseInt(localStorage.basketTimestamp, 10))
) { ) {
this.basket = []; this.basket = [];
} }

View File

@@ -18,7 +18,9 @@
#basket { #basket {
min-width: 300px; min-width: 300px;
border-radius: 8px; border-radius: 8px;
box-shadow: rgb(60 64 67 / 30%) 0 1px 3px 0, rgb(60 64 67 / 15%) 0 4px 8px 3px; box-shadow:
rgb(60 64 67 / 30%) 0 1px 3px 0,
rgb(60 64 67 / 15%) 0 4px 8px 3px;
padding: 10px; padding: 10px;
} }

View File

@@ -1,6 +1,6 @@
import { default as ForceGraph3D } from "3d-force-graph"; import { default as ForceGraph3D } from "3d-force-graph";
import { forceX, forceY, forceZ } from "d3-force-3d"; import { forceX, forceY, forceZ } from "d3-force-3d";
// biome-ignore lint/style/noNamespaceImport: This is how it should be imported // biome-ignore lint/performance/noNamespaceImport: This is how it should be imported
import * as Three from "three"; import * as Three from "three";
import SpriteText from "three-spritetext"; import SpriteText from "three-spritetext";

View File

@@ -15,7 +15,9 @@
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "GPL-3.0-only", "license": "GPL-3.0-only",
"sideEffects": [".css"], "sideEffects": [
".css"
],
"imports": { "imports": {
"#openapi": "./staticfiles/generated/openapi/client/index.ts", "#openapi": "./staticfiles/generated/openapi/client/index.ts",
"#core:*": "./core/static/bundled/*", "#core:*": "./core/static/bundled/*",

View File

@@ -1,4 +1,8 @@
import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history"; import {
getCurrentUrlParams,
History,
updateQueryString,
} from "#core:utils/history.ts";
import { ueFetchUeList } from "#openapi"; import { ueFetchUeList } from "#openapi";
const pageDefault = 1; const pageDefault = 1;
@@ -31,8 +35,8 @@ document.addEventListener("alpine:init", () => {
const url = getCurrentUrlParams(); const url = getCurrentUrlParams();
this.pushstate = History.Replace; this.pushstate = History.Replace;
this.page = Number.parseInt(url.get("page")) || pageDefault; this.page = Number.parseInt(url.get("page"), 10) || pageDefault;
this.page_size = Number.parseInt(url.get("page_size")) || pageSizeDefault; this.page_size = Number.parseInt(url.get("page_size"), 10) || pageSizeDefault;
this.search = url.get("search") || ""; this.search = url.get("search") || "";
this.department = url.getAll("department"); this.department = url.getAll("department");
this.credit_type = url.getAll("credit_type"); this.credit_type = url.getAll("credit_type");

View File

@@ -1,12 +1,12 @@
import { paginated } from "#core:utils/api"; import { paginated } from "#core:utils/api.ts";
import { History, initialUrlParams, updateQueryString } from "#core:utils/history"; import { History, initialUrlParams, updateQueryString } from "#core:utils/history.ts";
import { import {
type AlbumFetchAlbumData, type AlbumFetchAlbumData,
type AlbumSchema, type AlbumSchema,
albumFetchAlbum,
type PictureSchema, type PictureSchema,
type PicturesFetchPicturesData, type PicturesFetchPicturesData,
type PicturesUploadPictureErrors, type PicturesUploadPictureErrors,
albumFetchAlbum,
picturesFetchPictures, picturesFetchPictures,
picturesUploadPicture, picturesUploadPicture,
} from "#openapi"; } from "#openapi";
@@ -23,7 +23,7 @@ interface SubAlbumsConfig {
document.addEventListener("alpine:init", () => { document.addEventListener("alpine:init", () => {
Alpine.data("pictures", (config: AlbumPicturesConfig) => ({ Alpine.data("pictures", (config: AlbumPicturesConfig) => ({
pictures: [] as PictureSchema[], pictures: [] as PictureSchema[],
page: Number.parseInt(initialUrlParams.get("page")) || 1, page: Number.parseInt(initialUrlParams.get("page"), 10) || 1,
pushstate: History.Push /* Used to avoid pushing a state on a back action */, pushstate: History.Push /* Used to avoid pushing a state on a back action */,
loading: false, loading: false,
@@ -38,7 +38,10 @@ document.addEventListener("alpine:init", () => {
window.addEventListener("popstate", () => { window.addEventListener("popstate", () => {
this.pushstate = History.Replace; this.pushstate = History.Replace;
this.page = this.page =
Number.parseInt(new URLSearchParams(window.location.search).get("page")) || 1; Number.parseInt(
new URLSearchParams(window.location.search).get("page"),
10,
) || 1;
}); });
}, },

View File

@@ -1,7 +1,7 @@
import { AjaxSelect } from "#core:core/components/ajax-select-base";
import { registerComponent } from "#core:utils/web-components";
import type { TomOption } from "tom-select/dist/types/types"; import type { TomOption } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils"; import type { escape_html } from "tom-select/dist/types/utils";
import { AjaxSelect } from "#core:core/components/ajax-select-base.ts";
import { registerComponent } from "#core:utils/web-components.ts";
import { type AlbumAutocompleteSchema, albumAutocompleteAlbum } from "#openapi"; import { type AlbumAutocompleteSchema, albumAutocompleteAlbum } from "#openapi";
@registerComponent("album-ajax-select") @registerComponent("album-ajax-select")

View File

@@ -31,7 +31,7 @@ document.addEventListener("alpine:init", () => {
await Promise.all( await Promise.all(
this.downloadPictures.map((p: PictureSchema) => { this.downloadPictures.map((p: PictureSchema) => {
const imgName = `${p.album.name}/IMG_${p.id}_${p.date.replace(/[:\-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`; const imgName = `${p.album.name}/IMG_${p.id}_${p.date.replace(/[:-]/g, "_")}${p.name.slice(p.name.lastIndexOf("."))}`;
return zipWriter.add(imgName, new HttpReader(p.full_size_url), { return zipWriter.add(imgName, new HttpReader(p.full_size_url), {
level: 9, level: 9,
lastModDate: new Date(p.date), lastModDate: new Date(p.date),

View File

@@ -1,4 +1,4 @@
import { paginated } from "#core:utils/api"; import { paginated } from "#core:utils/api.ts";
import { import {
type PictureSchema, type PictureSchema,
type PicturesFetchPicturesData, type PicturesFetchPicturesData,
@@ -27,7 +27,7 @@ document.addEventListener("alpine:init", () => {
const lastCachedNumber = localStorage.getItem(localStorageInvalidationKey); const lastCachedNumber = localStorage.getItem(localStorageInvalidationKey);
if ( if (
lastCachedNumber !== null && lastCachedNumber !== null &&
Number.parseInt(lastCachedNumber) === config.nbPictures Number.parseInt(lastCachedNumber, 10) === config.nbPictures
) { ) {
return JSON.parse(localStorage.getItem(localStorageKey)); return JSON.parse(localStorage.getItem(localStorageKey));
} }

View File

@@ -1,21 +1,21 @@
import type { UserAjaxSelect } from "#core:core/components/ajax-select-index";
import { paginated } from "#core:utils/api";
import { exportToHtml } from "#core:utils/globals";
import { History } from "#core:utils/history";
import type TomSelect from "tom-select"; import type TomSelect from "tom-select";
import type { UserAjaxSelect } from "#core:core/components/ajax-select-index.ts";
import { paginated } from "#core:utils/api.ts";
import { exportToHtml } from "#core:utils/globals.ts";
import { History } from "#core:utils/history.ts";
import { import {
type IdentifiedUserSchema, type IdentifiedUserSchema,
type ModerationRequestSchema, type ModerationRequestSchema,
type PictureSchema, type PictureSchema,
type PicturesFetchIdentificationsResponse, type PicturesFetchIdentificationsResponse,
type PicturesFetchPicturesData, type PicturesFetchPicturesData,
type UserProfileSchema,
picturesDeletePicture, picturesDeletePicture,
picturesFetchIdentifications, picturesFetchIdentifications,
picturesFetchModerationRequests, picturesFetchModerationRequests,
picturesFetchPictures, picturesFetchPictures,
picturesIdentifyUsers, picturesIdentifyUsers,
picturesModeratePicture, picturesModeratePicture,
type UserProfileSchema,
usersidentifiedDeleteRelation, usersidentifiedDeleteRelation,
} from "#openapi"; } from "#openapi";
@@ -208,7 +208,8 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
} }
this.pushstate = History.Replace; this.pushstate = History.Replace;
this.currentPicture = this.pictures.find( this.currentPicture = this.pictures.find(
(i: PictureSchema) => i.id === Number.parseInt(event.state.sasPictureId), (i: PictureSchema) =>
i.id === Number.parseInt(event.state.sasPictureId, 10),
); );
}); });
this.pushstate = History.Replace; /* Avoid first url push */ this.pushstate = History.Replace; /* Avoid first url push */
@@ -301,7 +302,7 @@ exportToHtml("loadViewer", (config: ViewerConfig) => {
// biome-ignore lint/style/useNamingConvention: api is in snake_case // biome-ignore lint/style/useNamingConvention: api is in snake_case
picture_id: this.currentPicture.id, picture_id: this.currentPicture.id,
}, },
body: widget.items.map((i: string) => Number.parseInt(i)), body: widget.items.map((i: string) => Number.parseInt(i, 10)),
}); });
// refresh the identified users list // refresh the identified users list
await this.currentPicture.loadIdentifications({ forceReload: true }); await this.currentPicture.loadIdentifications({ forceReload: true });

View File

@@ -8,9 +8,9 @@ document.addEventListener("alpine:init", () => {
async init() { async init() {
const userSelect = document.getElementById("id_member") as HTMLSelectElement; const userSelect = document.getElementById("id_member") as HTMLSelectElement;
userSelect.addEventListener("change", async () => { userSelect.addEventListener("change", async () => {
await this.loadProfile(Number.parseInt(userSelect.value)); await this.loadProfile(Number.parseInt(userSelect.value, 10));
}); });
await this.loadProfile(Number.parseInt(userSelect.value)); await this.loadProfile(Number.parseInt(userSelect.value, 10));
}, },
async loadProfile(userId: number) { async loadProfile(userId: number) {

View File

@@ -1,4 +1,5 @@
import { BarController, BarElement, CategoryScale, Chart, LinearScale } from "chart.js"; import { BarController, BarElement, CategoryScale, Chart, LinearScale } from "chart.js";
Chart.register(BarController, BarElement, CategoryScale, LinearScale); Chart.register(BarController, BarElement, CategoryScale, LinearScale);
function getRandomColor() { function getRandomColor() {
@@ -18,13 +19,11 @@ function getRandomColorUniq(list: string[]) {
} }
function hexToRgb(hex: string) { function hexToRgb(hex: string) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
// biome-ignore lint/performance/useTopLevelRegex: Performance impact is minimal
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const hexrgb = hex.replace(shorthandRegex, (_m, r, g, b) => { const hexrgb = hex.replace(shorthandRegex, (_m, r, g, b) => {
return r + r + g + g + b + b; return r + r + g + g + b + b;
}); });
// biome-ignore lint/performance/useTopLevelRegex: Performance impact is minimal
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexrgb); const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexrgb);
return result return result
? { ? {

View File

@@ -63,10 +63,10 @@ function parseSlots(s: string): TimetableSlot[] {
} }
const [startHour, startMin] = parsed.groups.startHour const [startHour, startMin] = parsed.groups.startHour
.split(":") .split(":")
.map((i) => Number.parseInt(i)); .map((i) => Number.parseInt(i, 10));
const [endHour, endMin] = parsed.groups.endHour const [endHour, endMin] = parsed.groups.endHour
.split(":") .split(":")
.map((i) => Number.parseInt(i)); .map((i) => Number.parseInt(i, 10));
return { return {
...parsed.groups, ...parsed.groups,
startSlot: Math.floor((startHour * 60 + startMin) / MINUTES_PER_SLOT), startSlot: Math.floor((startHour * 60 + startMin) / MINUTES_PER_SLOT),
@@ -157,6 +157,7 @@ document.addEventListener("alpine:init", () => {
.map((c: TimetableSlot) => c.startHour) .map((c: TimetableSlot) => c.startHour)
.reduce((res: string, hour: string) => (hour < res ? hour : res), "24:00") .reduce((res: string, hour: string) => (hour < res ? hour : res), "24:00")
.split(":")[0], .split(":")[0],
10,
); );
const res: [string, object][] = []; const res: [string, object][] = [];
for (let i = 0; i <= this.endSlot - this.startSlot; i += 60 / MINUTES_PER_SLOT) { for (let i = 0; i <= this.endSlot - this.startSlot; i += 60 / MINUTES_PER_SLOT) {

View File

@@ -2,8 +2,8 @@
import { parse, resolve } from "node:path"; import { parse, resolve } from "node:path";
import inject from "@rollup/plugin-inject"; import inject from "@rollup/plugin-inject";
import { glob } from "glob"; import { glob } from "glob";
import { type AliasOptions, type UserConfig, defineConfig } from "vite";
import type { Rollup } from "vite"; import type { Rollup } from "vite";
import { type AliasOptions, defineConfig, type UserConfig } from "vite";
import tsconfig from "./tsconfig.json"; import tsconfig from "./tsconfig.json";
const outDir = resolve(__dirname, "./staticfiles/generated/bundled"); const outDir = resolve(__dirname, "./staticfiles/generated/bundled");