Add missing features

* Fix display
* Add internationalization
* Avoid querying under a certain amount of characters
* Update docs for translations with typescript
* Add interpolate to typescript globals
This commit is contained in:
Antoine Bartuccio 2024-10-16 02:22:27 +02:00
parent deda2b4055
commit 74a506c48b
7 changed files with 539 additions and 499 deletions

View File

@ -1,6 +1,6 @@
import "tom-select/dist/css/tom-select.css"; import "tom-select/dist/css/tom-select.css";
import TomSelect from "tom-select"; import TomSelect from "tom-select";
import type { TomItem, TomLoadCallback } from "tom-select/dist/types/types"; import type { TomItem, TomLoadCallback, 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 UserProfileSchema, userSearchUsers } from "#openapi"; import { type UserProfileSchema, userSearchUsers } from "#openapi";
@ -17,6 +17,7 @@ export class AjaxSelect extends HTMLSelectElement {
} }
loadTomSelect() { loadTomSelect() {
const minCharNumberForsearch = 2;
let maxItems = 1; let maxItems = 1;
if (this.multiple) { if (this.multiple) {
@ -25,12 +26,17 @@ export class AjaxSelect extends HTMLSelectElement {
this.widget = new TomSelect(this, { this.widget = new TomSelect(this, {
hideSelected: true, hideSelected: true,
diacritics: true,
duplicates: false,
maxItems: maxItems, maxItems: maxItems,
loadThrottle: Number.parseInt(this.dataset.delay) ?? null, loadThrottle: Number.parseInt(this.dataset.delay) ?? null,
valueField: "id", valueField: "id",
labelField: "display_name", labelField: "display_name",
searchField: ["display_name", "nick_name", "first_name", "last_name"], searchField: ["display_name", "nick_name", "first_name", "last_name"],
placeholder: this.dataset.placeholder ?? "", placeholder: this.dataset.placeholder ?? "",
shouldLoad: (query: string) => {
return query.length >= minCharNumberForsearch; // Avoid launching search with less than 2 characters
},
load: (query: string, callback: TomLoadCallback) => { load: (query: string, callback: TomLoadCallback) => {
userSearchUsers({ userSearchUsers({
query: { query: {
@ -62,6 +68,14 @@ export class AjaxSelect extends HTMLSelectElement {
item: (item: UserProfileSchema, sanitize: typeof escape_html) => { item: (item: UserProfileSchema, sanitize: typeof escape_html) => {
return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`; return `<span><i class="fa fa-times"></i>${sanitize(item.display_name)}</span>`;
}, },
// biome-ignore lint/style/useNamingConvention: that's how it's defined
not_loading: (data: TomOption, _sanitize: typeof escape_html) => {
return `<div class="no-results">${interpolate(gettext("You need to type %(number)s more characters"), { number: minCharNumberForsearch - data.input.length }, true)}</div>`;
},
// biome-ignore lint/style/useNamingConvention: that's how it's defined
no_results: (_data: TomOption, _sanitize: typeof escape_html) => {
return `<div class="no-results">${gettext("No results found")}</div>`;
},
}, },
}); });

View File

@ -3,6 +3,7 @@ import type { Alpine as AlpineType } from "alpinejs";
declare global { declare global {
const Alpine: AlpineType; const Alpine: AlpineType;
const gettext: (text: string) => string; const gettext: (text: string) => string;
const interpolate: <T>(fmt: string, args: string[] | T, isNamed?: boolean) => string;
} }
/** /**

View File

@ -24,6 +24,12 @@ Si le mot apparaît dans le template Jinja :
{% trans %}Hello{% endtrans %} {% trans %}Hello{% endtrans %}
``` ```
Si on est dans un fichier javascript ou typescript :
```js
gettext("Hello");
```
## Générer le fichier django.po ## Générer le fichier django.po
La traduction se fait en trois étapes. La traduction se fait en trois étapes.
@ -32,7 +38,7 @@ l'éditer et enfin le compiler au format binaire pour qu'il soit lu par le serve
```bash ```bash
./manage.py makemessages --locale=fr -e py,jinja --ignore=node_modules # Pour le backend ./manage.py makemessages --locale=fr -e py,jinja --ignore=node_modules # Pour le backend
./manage.py makemessages --locale=fr -d djangojs --ignore=node_modules # Pour le frontend ./manage.py makemessages --locale=fr -d djangojs -e js,ts --ignore=node_modules # Pour le frontend
``` ```
## Éditer le fichier django.po ## Éditer le fichier django.po

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-09 11:50+0200\n" "POT-Creation-Date: 2024-10-16 02:19+0200\n"
"PO-Revision-Date: 2024-09-17 11:54+0200\n" "PO-Revision-Date: 2024-09-17 11:54+0200\n"
"Last-Translator: Sli <antoine@bartuccio.fr>\n" "Last-Translator: Sli <antoine@bartuccio.fr>\n"
"Language-Team: AE info <ae.info@utbm.fr>\n" "Language-Team: AE info <ae.info@utbm.fr>\n"
@ -22,87 +22,95 @@ msgstr ""
msgid "captured.%s" msgid "captured.%s"
msgstr "capture.%s" msgstr "capture.%s"
#: core/static/webpack/easymde-index.js:32 #: core/static/webpack/ajax-select-index.ts:73
msgid "You need to type %(number)s more characters"
msgstr "Vous devez taper %(number)s caractères de plus"
#: core/static/webpack/ajax-select-index.ts:76
msgid "No results found"
msgstr "Aucun résultat trouvé"
#: core/static/webpack/easymde-index.ts:31
msgid "Heading" msgid "Heading"
msgstr "Titre" msgstr "Titre"
#: core/static/webpack/easymde-index.js:38 #: core/static/webpack/easymde-index.ts:37
msgid "Italic" msgid "Italic"
msgstr "Italique" msgstr "Italique"
#: core/static/webpack/easymde-index.js:44 #: core/static/webpack/easymde-index.ts:43
msgid "Bold" msgid "Bold"
msgstr "Gras" msgstr "Gras"
#: core/static/webpack/easymde-index.js:50 #: core/static/webpack/easymde-index.ts:49
msgid "Strikethrough" msgid "Strikethrough"
msgstr "Barré" msgstr "Barré"
#: core/static/webpack/easymde-index.js:59 #: core/static/webpack/easymde-index.ts:58
msgid "Underline" msgid "Underline"
msgstr "Souligné" msgstr "Souligné"
#: core/static/webpack/easymde-index.js:68 #: core/static/webpack/easymde-index.ts:67
msgid "Superscript" msgid "Superscript"
msgstr "Exposant" msgstr "Exposant"
#: core/static/webpack/easymde-index.js:77 #: core/static/webpack/easymde-index.ts:76
msgid "Subscript" msgid "Subscript"
msgstr "Indice" msgstr "Indice"
#: core/static/webpack/easymde-index.js:83 #: core/static/webpack/easymde-index.ts:82
msgid "Code" msgid "Code"
msgstr "Code" msgstr "Code"
#: core/static/webpack/easymde-index.js:90 #: core/static/webpack/easymde-index.ts:89
msgid "Quote" msgid "Quote"
msgstr "Citation" msgstr "Citation"
#: core/static/webpack/easymde-index.js:96 #: core/static/webpack/easymde-index.ts:95
msgid "Unordered list" msgid "Unordered list"
msgstr "Liste non ordonnée" msgstr "Liste non ordonnée"
#: core/static/webpack/easymde-index.js:102 #: core/static/webpack/easymde-index.ts:101
msgid "Ordered list" msgid "Ordered list"
msgstr "Liste ordonnée" msgstr "Liste ordonnée"
#: core/static/webpack/easymde-index.js:109 #: core/static/webpack/easymde-index.ts:108
msgid "Insert link" msgid "Insert link"
msgstr "Insérer lien" msgstr "Insérer lien"
#: core/static/webpack/easymde-index.js:115 #: core/static/webpack/easymde-index.ts:114
msgid "Insert image" msgid "Insert image"
msgstr "Insérer image" msgstr "Insérer image"
#: core/static/webpack/easymde-index.js:121 #: core/static/webpack/easymde-index.ts:120
msgid "Insert table" msgid "Insert table"
msgstr "Insérer tableau" msgstr "Insérer tableau"
#: core/static/webpack/easymde-index.js:128 #: core/static/webpack/easymde-index.ts:127
msgid "Clean block" msgid "Clean block"
msgstr "Nettoyer bloc" msgstr "Nettoyer bloc"
#: core/static/webpack/easymde-index.js:135 #: core/static/webpack/easymde-index.ts:134
msgid "Toggle preview" msgid "Toggle preview"
msgstr "Activer la prévisualisation" msgstr "Activer la prévisualisation"
#: core/static/webpack/easymde-index.js:141 #: core/static/webpack/easymde-index.ts:140
msgid "Toggle side by side" msgid "Toggle side by side"
msgstr "Activer la vue côte à côte" msgstr "Activer la vue côte à côte"
#: core/static/webpack/easymde-index.js:147 #: core/static/webpack/easymde-index.ts:146
msgid "Toggle fullscreen" msgid "Toggle fullscreen"
msgstr "Activer le plein écran" msgstr "Activer le plein écran"
#: core/static/webpack/easymde-index.js:154 #: core/static/webpack/easymde-index.ts:153
msgid "Markdown guide" msgid "Markdown guide"
msgstr "Guide markdown" msgstr "Guide markdown"
#: core/static/webpack/user/family-graph-index.js:222 #: core/static/webpack/user/family-graph-index.js:233
msgid "family_tree.%(extension)s" msgid "family_tree.%(extension)s"
msgstr "arbre_genealogique.%(extension)s" msgstr "arbre_genealogique.%(extension)s"
#: core/static/webpack/user/pictures-index.js:67 #: core/static/webpack/user/pictures-index.js:76
msgid "pictures.%(extension)s" msgid "pictures.%(extension)s"
msgstr "photos.%(extension)s" msgstr "photos.%(extension)s"
@ -110,10 +118,10 @@ msgstr "photos.%(extension)s"
msgid "Incorrect value" msgid "Incorrect value"
msgstr "Valeur incorrecte" msgstr "Valeur incorrecte"
#: sas/static/sas/js/viewer.js:205 #: sas/static/webpack/sas/viewer-index.ts:271
msgid "Couldn't moderate picture" msgid "Couldn't moderate picture"
msgstr "Il n'a pas été possible de modérer l'image" msgstr "Il n'a pas été possible de modérer l'image"
#: sas/static/sas/js/viewer.js:217 #: sas/static/webpack/sas/viewer-index.ts:284
msgid "Couldn't delete picture" msgid "Couldn't delete picture"
msgstr "Il n'a pas été possible de supprimer l'image" msgstr "Il n'a pas été possible de supprimer l'image"

View File

@ -29,7 +29,7 @@
width: 100%; width: 100%;
} }
> .photo { >.photo {
box-sizing: border-box; box-sizing: border-box;
height: 500px; height: 500px;
display: flex; display: flex;
@ -42,7 +42,7 @@
height: auto; height: auto;
} }
> img { >img {
height: 100%; height: 100%;
max-width: 100%; max-width: 100%;
object-fit: contain; object-fit: contain;
@ -57,7 +57,7 @@
width: 100%; width: 100%;
} }
> .navigation { >.navigation {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
gap: 10px; gap: 10px;
@ -66,8 +66,8 @@
width: 100%; width: 100%;
} }
> #prev, >#prev,
> #next { >#next {
width: calc(50% - 5px); width: calc(50% - 5px);
aspect-ratio: 16/9; aspect-ratio: 16/9;
background: #333333; background: #333333;
@ -80,6 +80,7 @@
object-fit: cover; object-fit: cover;
opacity: 70%; opacity: 70%;
} }
.overlay { .overlay {
position: absolute; position: absolute;
top: 50%; top: 50%;
@ -89,7 +90,7 @@
font-size: 40px; font-size: 40px;
} }
> div { >div {
display: flex; display: flex;
position: relative; position: relative;
width: 100%; width: 100%;
@ -98,12 +99,12 @@
} }
} }
> .tags { >.tags {
@media (min-width: 1001px) { @media (min-width: 1001px) {
margin-right: 5px; margin-right: 5px;
} }
> ul { >ul {
list-style-type: none; list-style-type: none;
margin: 0; margin: 0;
display: flex; display: flex;
@ -118,7 +119,7 @@
margin-right: 5px; margin-right: 5px;
} }
> li { >li {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -135,7 +136,7 @@
max-width: calc(50% - 5px); max-width: calc(50% - 5px);
} }
> a { >a {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -155,7 +156,7 @@
background-color: #aaa; background-color: #aaa;
} }
> span { >span {
width: 100%; width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -167,14 +168,14 @@
margin-left: 10px; margin-left: 10px;
} }
> img { >img {
width: 25px; width: 25px;
max-height: 25px; max-height: 25px;
object-fit: contain; object-fit: contain;
border-radius: 50%; border-radius: 50%;
} }
> .profile-pic { >.profile-pic {
background-position: center center; background-position: center center;
background-size: cover; background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
@ -187,23 +188,24 @@
} }
} }
> form { >form {
> p { >p {
box-sizing: border-box; box-sizing: border-box;
} }
> .results_on_deck > div { >.results_on_deck>div {
position: relative; position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
word-break: break-word; word-break: break-word;
> span { >span {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
} }
} }
input { input {
min-width: 100%; min-width: 100%;
max-width: 100%; max-width: 100%;
@ -226,17 +228,17 @@
flex-direction: column; flex-direction: column;
} }
> .infos { >.infos {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 50%; width: 50%;
> div > div { >div>div {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
> *:first-child { >*:first-child {
min-width: 150px; min-width: 150px;
@media (max-width: 1000px) { @media (max-width: 1000px) {
@ -246,18 +248,18 @@
} }
} }
> .tools { >.tools {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 50%; width: 50%;
> div { >div {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
> div { >div {
> a.button { >a.button {
box-sizing: border-box; box-sizing: border-box;
background-color: #f2f2f2; background-color: #f2f2f2;
display: flex; display: flex;
@ -274,7 +276,7 @@
} }
} }
> a.text.danger { >a.text.danger {
color: red; color: red;
&:hover { &:hover {

View File

@ -3,11 +3,10 @@
{%- block additional_css -%} {%- block additional_css -%}
<link rel="stylesheet" href="{{ static('webpack/ajax-select-index.css') }}"> <link rel="stylesheet" href="{{ static('webpack/ajax-select-index.css') }}">
<link rel="stylesheet" href="{{ static('sas/css/picture.scss') }}"> <link rel="stylesheet" href="{{ static('sas/css/picture.scss') }}">
<link rel="stylesheet" href="{{ static('user/user_stats.scss') }}">
{%- endblock -%} {%- endblock -%}
{%- block additional_js -%} {%- block additional_js -%}
<script src="{{ static('webpack/ajax-select-index.ts') }}"></script> <script defer src="{{ static('webpack/ajax-select-index.ts') }}"></script>
<script defer src="{{ static("webpack/sas/viewer-index.ts") }}"></script> <script defer src="{{ static("webpack/sas/viewer-index.ts") }}"></script>
{%- endblock -%} {%- endblock -%}
@ -158,7 +157,13 @@
<h5>{% trans %}People{% endtrans %}</h5> <h5>{% trans %}People{% endtrans %}</h5>
{% if user.was_subscribed %} {% if user.was_subscribed %}
<form @submit.prevent="submitIdentification" x-show="!!selector"> <form @submit.prevent="submitIdentification" x-show="!!selector">
<select x-ref="search" is="ajax-select" multiple data-delay="300" data-placeholder="{%- trans -%}Identify users on pictures{%- endtrans -%}"></select> <select
x-ref="search"
is="ajax-select"
multiple
data-delay="300"
data-placeholder="{%- trans -%}Identify users on pictures{%- endtrans -%}"
></select>
<input type="submit" value="{% trans %}Go{% endtrans %}"/> <input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form> </form>
{% endif %} {% endif %}