improve $notifications

This commit is contained in:
imperosol
2026-05-16 20:03:41 +02:00
parent 899fc90821
commit 68aa4515f9
8 changed files with 62 additions and 40 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<title>{% trans %}Slideshow{% endtrans %}</title> <title>{% trans %}Slideshow{% endtrans %}</title>
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}"> <link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
<link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" /> <link href="{{ static('css/slideshow.scss') }}" rel="stylesheet" type="text/css" />
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script> <script type="module" src="{{ static('bundled/alpine-index.ts') }}"></script>
<script type="module" src="{{ static('bundled/com/slideshow-index.ts') }}"></script> <script type="module" src="{{ static('bundled/com/slideshow-index.ts') }}"></script>
</head> </head>
<body x-data="slideshow([ <body x-data="slideshow([
-12
View File
@@ -1,12 +0,0 @@
import sort from "@alpinejs/sort";
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.magic("notifications", notificationPlugin);
window.Alpine = Alpine;
window.addEventListener("DOMContentLoaded", () => {
Alpine.start();
});
+21
View File
@@ -0,0 +1,21 @@
import sort from "@alpinejs/sort";
import { Alpine } from "alpinejs";
import { limitedChoices } from "#core:alpine/limited-choices";
import {
type NotificationPlugin,
alpinePlugin as notificationPlugin,
} from "#core:utils/notifications";
declare module "alpinejs" {
interface Magics<T> {
$notifications: NotificationPlugin;
}
}
Alpine.plugin([sort, limitedChoices]);
Alpine.magic("notifications", notificationPlugin);
// biome-ignore lint/style/useNamingConvention: it's how it's named
Object.assign(window, { Alpine });
window.addEventListener("DOMContentLoaded", () => {
Alpine.start();
});
+32 -17
View File
@@ -1,30 +1,40 @@
import { Alpine } from "alpinejs";
export enum NotificationLevel { export enum NotificationLevel {
Error = "error", Error = "error",
Warning = "warning", Warning = "warning",
Success = "success", Success = "success",
} }
export function createNotification(message: string, level: NotificationLevel) { export interface NotificationPlugin {
const element = document.getElementById("quick-notifications"); error: (message: string) => void;
if (element === null) { warning: (message: string) => void;
return false; success: (message: string) => void;
} clear: () => void;
return element.dispatchEvent( addMany: (notifs: Notification[]) => void;
new CustomEvent("quick-notification-add", { getAll: () => Notification[];
detail: { text: message, tag: level },
}),
);
} }
export function deleteNotifications() { export interface Notification {
const element = document.getElementById("quick-notifications"); tag: NotificationLevel;
if (element === null) { text: string;
return false;
}
return element.dispatchEvent(new CustomEvent("quick-notification-delete"));
} }
export function alpinePlugin() { Alpine.store("notifications", [] as Notification[]);
function createNotification(message: string, level: NotificationLevel) {
(Alpine.store("notifications") as Notification[]).push({ text: message, tag: level });
}
function deleteNotifications() {
Alpine.store("notifications", []);
}
function getNotifications() {
return Alpine.store("notifications") as Notification[];
}
export function alpinePlugin(): NotificationPlugin {
return { return {
error: (message: string) => createNotification(message, NotificationLevel.Error), error: (message: string) => createNotification(message, NotificationLevel.Error),
warning: (message: string) => warning: (message: string) =>
@@ -32,5 +42,10 @@ export function alpinePlugin() {
success: (message: string) => success: (message: string) =>
createNotification(message, NotificationLevel.Success), createNotification(message, NotificationLevel.Success),
clear: () => deleteNotifications(), clear: () => deleteNotifications(),
addMany: (notifs: Notification[]) =>
notifs.forEach((n) => {
createNotification(n.text, n.tag);
}),
getAll: () => getNotifications(),
}; };
} }
+1 -1
View File
@@ -37,7 +37,7 @@
<script src="{{ url('javascript-catalog') }}"></script> <script src="{{ url('javascript-catalog') }}"></script>
<script type="module" src="{{ static("bundled/core/navbar-index.ts") }}"></script> <script type="module" src="{{ static("bundled/core/navbar-index.ts") }}"></script>
<script type="module" src="{{ static("bundled/core/components/include-index.ts") }}"></script> <script type="module" src="{{ static("bundled/core/components/include-index.ts") }}"></script>
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script> <script type="module" src="{{ static('bundled/alpine-index.ts') }}"></script>
<script type="module" src="{{ static('bundled/htmx-index.js') }}"></script> <script type="module" src="{{ static('bundled/htmx-index.js') }}"></script>
<script type="module" src="{{ static('bundled/country-flags-index.ts') }}"></script> <script type="module" src="{{ static('bundled/country-flags-index.ts') }}"></script>
<script type="module" src="{{ static('bundled/core/tooltips-index.ts') }}"></script> <script type="module" src="{{ static('bundled/core/tooltips-index.ts') }}"></script>
+4 -6
View File
@@ -1,15 +1,13 @@
<div id="quick-notifications" <div id="quick-notifications"
x-data='{ x-data='{ messages: $notifications.getAll() }'
messages: [ x-init='$notifications.addMany([
{%- for message in messages -%} {%- for message in messages -%}
{%- if not message.extra_tags -%} {%- if not message.extra_tags -%}
{ tag: {{ message.tags|string|tojson }}, text: {{ message|string|tojson }} }, { tag: {{ message.tags|string|tojson }}, text: {{ message|string|tojson }} },
{%- endif -%} {%- endif -%}
{%- endfor -%} {%- endfor -%}
] ])'
}' >
@quick-notification-add="(e) => messages.push(e?.detail)"
@quick-notification-delete="messages = []">
<template x-for="(message, index) in messages"> <template x-for="(message, index) in messages">
<div class="alert" :class="`alert-${message.tag}`" x-transition> <div class="alert" :class="`alert-${message.tag}`" x-transition>
<span class="alert-main" x-text="message.text"></span> <span class="alert-main" x-text="message.text"></span>
+2 -2
View File
@@ -35,8 +35,8 @@ les fichiers sont à mettre dans un dossier `static/bundled` de l'application à
Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `bundled/` comme prefix. Pour accéder au fichier, il faut utiliser `static` comme pour le reste mais en ajouter `bundled/` comme prefix.
```jinja ```jinja
{# Example pour ajouter sith/core/bundled/alpine-index.js #} {# Example pour ajouter sith/core/bundled/alpine-index.ts #}
<script type="module" src="{{ static('bundled/alpine-index.js') }}"></script> <script type="module" src="{{ static('bundled/alpine-index.ts') }}"></script>
<script type="module" src="{{ static('bundled/other-index.ts') }}"></script> <script type="module" src="{{ static('bundled/other-index.ts') }}"></script>
``` ```
+1 -1
View File
@@ -32,7 +32,7 @@ class JsBundlerManifestEntry:
# because that's what the user types when importing statics and that's what django gives us # because that's what the user types when importing statics and that's what django gives us
# This is really similar to what we are doing in the bundler, it uses a similar algorithm # This is really similar to what we are doing in the bundler, it uses a similar algorithm
# Example: # Example:
# core/static/bundled/alpine-index.js -> bundled/alpine-index.js # core/static/bundled/alpine-index.ts -> bundled/alpine-index.ts
# core/static/bundled/components/include-index.ts -> core/static/bundled/components/include-index.ts # core/static/bundled/components/include-index.ts -> core/static/bundled/components/include-index.ts
def get_relative_src_name(name: str) -> str: def get_relative_src_name(name: str) -> str:
original_path = Path(name) original_path = Path(name)