@jjlmoya/utils-cooking 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +58 -58
- package/src/tests/schemas_fulfillment.test.ts +23 -0
- package/src/tests/title_quality.test.ts +55 -0
- package/src/tool/american-kitchen-converter/i18n/fr.ts +1 -1
- package/src/tool/banana-ripeness/i18n/fr.ts +86 -86
- package/src/tool/brine/component.astro +20 -22
- package/src/tool/cookware-guide/component.astro +22 -6
- package/src/tool/cookware-guide/i18n/en.ts +2 -2
- package/src/tool/cookware-guide/i18n/es.ts +3 -3
- package/src/tool/cookware-guide/i18n/fr.ts +109 -110
- package/src/tool/ingredient-rescaler/component.astro +8 -3
- package/src/tool/ingredient-rescaler/i18n/en.ts +74 -97
- package/src/tool/ingredient-rescaler/i18n/es.ts +77 -100
- package/src/tool/kitchen-timer/component.astro +27 -9
- package/src/tool/kitchen-timer/i18n/en.ts +6 -7
- package/src/tool/kitchen-timer/i18n/es.ts +7 -8
- package/src/tool/kitchen-timer/i18n/fr.ts +76 -77
- package/src/tool/kitchen-timer/init.ts +23 -6
- package/src/tool/kitchen-timer/lib/KitchenTimer.ts +20 -8
- package/src/tool/meringue-peak/component.astro +4 -4
- package/src/tool/meringue-peak/i18n/fr.ts +1 -1
- package/src/tool/mold-scaler/component.astro +17 -11
- package/src/tool/mold-scaler/i18n/en.ts +87 -60
- package/src/tool/mold-scaler/i18n/es.ts +87 -61
- package/src/tool/mold-scaler/i18n/fr.ts +97 -69
- package/src/tool/pizza/i18n/en.ts +2 -2
- package/src/tool/pizza/i18n/es.ts +2 -2
- package/src/tool/pizza/i18n/fr.ts +2 -2
- package/src/tool/roux-guide/i18n/en.ts +18 -1
- package/src/tool/roux-guide/i18n/es.ts +21 -4
- package/src/tool/roux-guide/i18n/fr.ts +18 -1
- package/src/tool/roux-guide/init.ts +55 -52
- package/src/tool/sourdough-calculator/i18n/es.ts +133 -133
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
import type { ToolLocaleContent } from '../../../types';
|
|
2
2
|
|
|
3
|
-
const title = "
|
|
4
|
-
const description = "
|
|
3
|
+
const title = "Minuteur de Cuisine Multiple";
|
|
4
|
+
const description = "Gérez plusieurs temps de cuisson simultanément. Alarmes indépendantes, idéal pour les chefs et l'organisation de la cuisine (Mise en Place).";
|
|
5
5
|
const faq = [
|
|
6
6
|
{
|
|
7
|
-
question: '
|
|
7
|
+
question: 'Combien de minuteurs puis-je créer ?',
|
|
8
8
|
answer:
|
|
9
|
-
'
|
|
9
|
+
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément : bouillir des pâtes, réduire une sauce, reposer la viande et cuire au four, tout en même temps.',
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
|
-
question: '
|
|
12
|
+
question: 'Pourquoi le temps de repos est-il important pour la viande ?',
|
|
13
13
|
answer:
|
|
14
|
-
'
|
|
14
|
+
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat : une viande juteuse au lieu d\'être sèche. Le repos est une cuisson passive.',
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
|
-
question: '
|
|
17
|
+
question: 'Fonctionne-t-il avec l\'écran verrouillé ?',
|
|
18
18
|
answer:
|
|
19
|
-
'
|
|
19
|
+
'Oui, mais vous devez accorder les permissions de notification. Les minuteurs continuent de fonctionner en arrière-plan et vous alerteront avec un son et une notification de navigateur même si vous changez d\'onglet ou verrouillez votre téléphone.',
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
question: '
|
|
22
|
+
question: 'Qu\'est-ce que la "Zone de Danger" alimentaire ?',
|
|
23
23
|
answer:
|
|
24
|
-
'
|
|
24
|
+
'Entre 5°C et 65°C, les bactéries se multiplient rapidement. Les aliments cuits ne doivent pas rester dans cette plage plus de 2 heures (1 heure si la température dépasse 30°C). Utilisez un minuteur pour surveiller le refroidissement avant de réfrigérer.',
|
|
25
25
|
},
|
|
26
26
|
];
|
|
27
27
|
const howTo = [
|
|
28
28
|
{
|
|
29
|
-
name: '
|
|
30
|
-
text: '
|
|
29
|
+
name: 'Créer plusieurs minuteurs',
|
|
30
|
+
text: 'Utilisez le bouton "+" pour ajouter autant de minuteurs que nécessaire. Parfait pour orchestrer plusieurs plats simultanément.',
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
|
-
name: '
|
|
34
|
-
text: '
|
|
33
|
+
name: 'Personnaliser chaque minuteur',
|
|
34
|
+
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit : "Four", "Riz", "Sauce", etc.',
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
name: '
|
|
38
|
-
text: '
|
|
37
|
+
name: 'Contrôler depuis le dock mobile',
|
|
38
|
+
text: 'Sur mobile, les minuteurs actifs apparaissent dans un dock coulissant en bas. Mettez en pause ou redémarrez directement depuis là.',
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
name: '
|
|
42
|
-
text: '
|
|
41
|
+
name: 'Recevoir des notifications',
|
|
42
|
+
text: 'Autorisez les notifications pour que le navigateur vous alerte quand le temps est écoulé, même si vous changez d\'onglet.',
|
|
43
43
|
},
|
|
44
44
|
];
|
|
45
45
|
|
|
@@ -76,154 +76,153 @@ const appSchema = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
export const content: ToolLocaleContent = {
|
|
79
|
-
slug: '
|
|
80
|
-
title: '
|
|
79
|
+
slug: 'minuteur-cuisine',
|
|
80
|
+
title: 'Minuteur de Cuisine Multiple',
|
|
81
81
|
description:
|
|
82
|
-
'
|
|
82
|
+
'Gérez plusieurs temps de cuisson simultanément. Alarmes indépendantes, idéal pour les chefs et l\'organisation de la cuisine (Mise en Place).',
|
|
83
83
|
faqTitle: 'Questions Fréquemment Posées',
|
|
84
84
|
bibliographyTitle: 'Références',
|
|
85
85
|
|
|
86
86
|
ui: {
|
|
87
|
-
addTimer: '
|
|
88
|
-
stopAll: '
|
|
89
|
-
defaultName: '
|
|
90
|
-
newTimerName: '
|
|
91
|
-
timerDefault1: '
|
|
92
|
-
timerDefault2: '
|
|
93
|
-
timerDefault3: '
|
|
94
|
-
label: '
|
|
95
|
-
hours: '
|
|
87
|
+
addTimer: 'Ajouter',
|
|
88
|
+
stopAll: 'Tout Arrêter',
|
|
89
|
+
defaultName: 'Minuteur',
|
|
90
|
+
newTimerName: 'Nouveau Minuteur',
|
|
91
|
+
timerDefault1: 'Minuteur 1',
|
|
92
|
+
timerDefault2: 'Minuteur 2',
|
|
93
|
+
timerDefault3: 'Minuteur 3',
|
|
94
|
+
label: 'Étiquette',
|
|
95
|
+
hours: 'Heures',
|
|
96
96
|
minutes: 'Min',
|
|
97
97
|
seconds: 'Sec',
|
|
98
|
-
ready: '
|
|
99
|
-
start: '
|
|
98
|
+
ready: 'Prêt',
|
|
99
|
+
start: 'Démarrer',
|
|
100
100
|
pause: 'Pause',
|
|
101
|
-
reset: '
|
|
101
|
+
reset: 'Réinitialiser',
|
|
102
102
|
addOneMin: '+1 min',
|
|
103
103
|
addFiveMin: '+5 min',
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
104
|
+
statusReady: 'Prêt',
|
|
105
|
+
statusRunning: 'En cours',
|
|
106
|
+
statusPaused: 'Pause',
|
|
107
|
+
statusFinished: 'FINI !',
|
|
108
|
+
finishNotification: 'Minuteur Terminé pour',
|
|
110
109
|
},
|
|
111
110
|
|
|
112
111
|
faq: [
|
|
113
112
|
{
|
|
114
|
-
question: '
|
|
113
|
+
question: 'Combien de minuteurs puis-je créer ?',
|
|
115
114
|
answer:
|
|
116
|
-
'
|
|
115
|
+
'Illimité. Ajoutez-en autant que nécessaire avec le bouton "+". Parfait pour cuisiner plusieurs plats simultanément : bouillir des pâtes, réduire une sauce, reposer la viande et cuire au four, tout en même temps.',
|
|
117
116
|
},
|
|
118
117
|
{
|
|
119
|
-
question: '
|
|
118
|
+
question: 'Pourquoi le temps de repos est-il important pour la viande ?',
|
|
120
119
|
answer:
|
|
121
|
-
'
|
|
120
|
+
'Lorsque vous coupez la viande juste après la cuisson, les jus s\'échappent sur l\'assiette. Si vous la laissez reposer 5 à 10 minutes, les fibres se détendent et les jus se redistribuent. Résultat : une viande juteuse au lieu d\'être sèche. Le repos est une cuisson passive.',
|
|
122
121
|
},
|
|
123
122
|
{
|
|
124
|
-
question: '
|
|
123
|
+
question: 'Fonctionne-t-il avec l\'écran verrouillé ?',
|
|
125
124
|
answer:
|
|
126
|
-
'
|
|
125
|
+
'Oui, mais vous devez accorder les permissions de notification. Les minuteurs continuent de fonctionner en arrière-plan et vous alerteront avec un son et une notification de navigateur même si vous changez d\'onglet ou verrouillez votre téléphone.',
|
|
127
126
|
},
|
|
128
127
|
{
|
|
129
|
-
question: '
|
|
128
|
+
question: 'Qu\'est-ce que la "Zone de Danger" alimentaire ?',
|
|
130
129
|
answer:
|
|
131
|
-
'
|
|
130
|
+
'Entre 5°C et 65°C, les bactéries se multiplient rapidement. Les aliments cuits ne doivent pas rester dans cette plage plus de 2 heures (1 heure si la température dépasse 30°C). Utilisez un minuteur pour surveiller le refroidissement avant de réfrigérer.',
|
|
132
131
|
},
|
|
133
132
|
],
|
|
134
133
|
|
|
135
134
|
bibliography: [
|
|
136
135
|
{
|
|
137
|
-
name: '
|
|
136
|
+
name: 'Sécurité Alimentaire : Directives de l\'USDA',
|
|
138
137
|
url: 'https://www.fsis.usda.gov/',
|
|
139
138
|
},
|
|
140
139
|
{
|
|
141
|
-
name: 'Mise en Place -
|
|
140
|
+
name: 'Mise en Place - Cuisine Professionnelle (Escoffier)',
|
|
142
141
|
url: 'https://www.escoffier.edu/',
|
|
143
142
|
},
|
|
144
143
|
],
|
|
145
144
|
|
|
146
145
|
howTo: [
|
|
147
146
|
{
|
|
148
|
-
name: '
|
|
149
|
-
text: '
|
|
147
|
+
name: 'Créer plusieurs minuteurs',
|
|
148
|
+
text: 'Utilisez le bouton "+" pour ajouter autant de minuteurs que nécessaire. Parfait pour orchestrer plusieurs plats simultanément.',
|
|
150
149
|
},
|
|
151
150
|
{
|
|
152
|
-
name: '
|
|
153
|
-
text: '
|
|
151
|
+
name: 'Personnaliser chaque minuteur',
|
|
152
|
+
text: 'Changez le nom de chaque minuteur pour identifier ce qui cuit : "Four", "Riz", "Sauce", etc.',
|
|
154
153
|
},
|
|
155
154
|
{
|
|
156
|
-
name: '
|
|
157
|
-
text: '
|
|
155
|
+
name: 'Contrôler depuis le dock mobile',
|
|
156
|
+
text: 'Sur mobile, les minuteurs actifs apparaissent dans un dock coulissant en bas. Mettez en pause ou redémarrez directement depuis là.',
|
|
158
157
|
},
|
|
159
158
|
{
|
|
160
|
-
name: '
|
|
161
|
-
text: '
|
|
159
|
+
name: 'Recevoir des notifications',
|
|
160
|
+
text: 'Autorisez les notifications pour que le navigateur vous alerte quand le temps est écoulé, même si vous changez d\'onglet.',
|
|
162
161
|
},
|
|
163
162
|
],
|
|
164
163
|
|
|
165
164
|
seo: [
|
|
166
165
|
{
|
|
167
166
|
type: 'title',
|
|
168
|
-
text: '
|
|
167
|
+
text: 'Maîtrise Temporelle en Cuisine',
|
|
169
168
|
level: 2,
|
|
170
169
|
},
|
|
171
170
|
{
|
|
172
171
|
type: 'paragraph',
|
|
173
|
-
html: '
|
|
172
|
+
html: 'La cuisine professionnelle ne repose pas uniquement sur les recettes, mais sur une <strong>gestion précise du temps</strong>. Le concept français de <em>"Mise en Place"</em> (chaque chose à sa place) inclut le temps comme ingrédient. Un steak qui repose 5 minutes redistribue ses jus et devient tendre ; un steak qui ne repose pas les perd à la première découpe.',
|
|
174
173
|
},
|
|
175
174
|
{
|
|
176
175
|
type: 'paragraph',
|
|
177
|
-
html: '
|
|
176
|
+
html: 'Cet outil de <strong>Minuteur Multiple</strong> a été conçu pour les chefs à domicile et les professionnels qui ont besoin d\'orchestrer une symphonie de plats : bouillir des pâtes, rôti au four et réduction de sauce, tout cela se passant simultanément.',
|
|
178
177
|
},
|
|
179
178
|
{
|
|
180
179
|
type: 'title',
|
|
181
|
-
text: '
|
|
180
|
+
text: 'Le Rôle de la Température et du Temps',
|
|
182
181
|
level: 3,
|
|
183
182
|
},
|
|
184
183
|
{
|
|
185
184
|
type: 'paragraph',
|
|
186
|
-
html: '
|
|
185
|
+
html: 'Cuisiner consiste essentiellement à appliquer de la chaleur pendant une durée spécifique pour transformer la structure chimique des aliments. La dénaturation des protéines et la réaction de Maillard (qui donne la couleur dorée et la saveur) dépendent de manière critique du minuteur.',
|
|
187
186
|
},
|
|
188
187
|
{
|
|
189
188
|
type: 'table',
|
|
190
|
-
headers: ['
|
|
189
|
+
headers: ['Aliment', 'Point de Cuisson', 'Temps', 'Bénéfice'],
|
|
191
190
|
rows: [
|
|
192
|
-
['
|
|
193
|
-
['
|
|
194
|
-
['
|
|
195
|
-
['
|
|
196
|
-
['
|
|
197
|
-
['
|
|
191
|
+
['Œufs à la coque', 'Jaune liquide', '3-4 min', 'Protéine douce, digestion facile'],
|
|
192
|
+
['Œufs mollets', 'Jaune dense', '5-6 min', 'Équilibre parfait de texture'],
|
|
193
|
+
['Œufs durs', 'Entièrement pris', '9-11 min', 'Fermeté maximale'],
|
|
194
|
+
['Viande - Saisie', 'Feu vif', '1-2 min/face', 'Réaction de Maillard, jutosité'],
|
|
195
|
+
['Viande - Repos fin', 'Repos', '5 min', 'Redistribution des jus'],
|
|
196
|
+
['Viande - Grand Repos', 'Repos', '15-20 min', 'Fibres détendues'],
|
|
198
197
|
],
|
|
199
198
|
},
|
|
200
199
|
{
|
|
201
200
|
type: 'title',
|
|
202
|
-
text: '
|
|
201
|
+
text: 'Sécurité Alimentaire : La Zone de Danger',
|
|
203
202
|
level: 3,
|
|
204
203
|
},
|
|
205
204
|
{
|
|
206
205
|
type: 'paragraph',
|
|
207
|
-
html: '
|
|
206
|
+
html: 'Le temps n\'affecte pas seulement la qualité, il affecte la sécurité. La "Zone de Danger" bactérienne se situe entre 5°C et 65°C. Les aliments cuits ne doivent pas rester dans cette plage pendant plus de <strong>2 heures</strong> (ou 1 heure si la température ambiante dépasse 30°C). Utilisez un minuteur pour surveiller le refroidissement avant de ranger les aliments au réfrigérateur.',
|
|
208
207
|
},
|
|
209
208
|
{
|
|
210
209
|
type: 'title',
|
|
211
|
-
text: '
|
|
210
|
+
text: 'Conseils d\'Organisation Professionnelle',
|
|
212
211
|
level: 3,
|
|
213
212
|
},
|
|
214
213
|
{
|
|
215
214
|
type: 'list',
|
|
216
215
|
items: [
|
|
217
|
-
'<strong
|
|
218
|
-
'<strong>
|
|
219
|
-
'<strong>
|
|
216
|
+
'<strong>Échelonnez les temps de fin :</strong> Si tout se termine en même temps, vous vous sentirez débordé pour le dressage. Essayez d\'avoir les accompagnements prêts 5 minutes avant le plat principal.',
|
|
217
|
+
'<strong>Utilisez la chaleur résiduelle :</strong> Éteignez les légumes ou les pâtes 1 à 2 minutes avant que le minuteur ne sonne. La chaleur résiduelle finira la cuisson en douceur.',
|
|
218
|
+
'<strong>Nommez vos minuteurs :</strong> Dans une cuisine occupée, il est facile d\'oublier quelle alarme correspond à quoi. Utilisez la fonction de renommage de cet outil pour étiqueter "Four", "Riz" ou "Sauce".',
|
|
220
219
|
],
|
|
221
220
|
},
|
|
222
221
|
{
|
|
223
222
|
type: 'tip',
|
|
224
|
-
html: '<strong>
|
|
223
|
+
html: '<strong>Conseil de pro :</strong> La précision temporelle est ce qui sépare un chef d\'un cuisinier amateur. Investissez dans un bon minuteur et utilisez-le toujours. L\'expérience vous dit quand quelque chose "semble prêt", mais le temps garantit la régularité.',
|
|
225
224
|
},
|
|
226
225
|
],
|
|
227
226
|
|
|
228
|
-
schemas: [faqSchema, howToSchema, appSchema],
|
|
227
|
+
schemas: [faqSchema as any, howToSchema as any, appSchema as any],
|
|
229
228
|
};
|
|
@@ -3,7 +3,8 @@ import { KitchenTimer } from "./lib/KitchenTimer";
|
|
|
3
3
|
function setupAddTimerButton(
|
|
4
4
|
timerTemplate: HTMLTemplateElement,
|
|
5
5
|
timersGrid: HTMLElement,
|
|
6
|
-
activeTimers: KitchenTimer[]
|
|
6
|
+
activeTimers: KitchenTimer[],
|
|
7
|
+
ui: Record<string, string>
|
|
7
8
|
) {
|
|
8
9
|
document.getElementById("add-timer-btn")?.addEventListener("click", () => {
|
|
9
10
|
if (!timerTemplate || !timersGrid) return;
|
|
@@ -14,11 +15,19 @@ function setupAddTimerButton(
|
|
|
14
15
|
const count = activeTimers.length + 1;
|
|
15
16
|
newCard.setAttribute("data-index", count.toString());
|
|
16
17
|
const nameInput = newCard.querySelector(".timer-name") as HTMLInputElement;
|
|
17
|
-
if (nameInput) nameInput.value =
|
|
18
|
+
if (nameInput) nameInput.value = `${ui.defaultName} ${count}`;
|
|
18
19
|
|
|
19
20
|
timersGrid.appendChild(newCard);
|
|
20
21
|
|
|
21
|
-
const timer = new KitchenTimer(newCard
|
|
22
|
+
const timer = new KitchenTimer(newCard, {
|
|
23
|
+
ready: ui.statusReady as string,
|
|
24
|
+
running: ui.statusRunning as string,
|
|
25
|
+
paused: ui.statusPaused as string,
|
|
26
|
+
finished: ui.statusFinished as string,
|
|
27
|
+
start: ui.start as string,
|
|
28
|
+
pause: ui.pause as string,
|
|
29
|
+
finishNotification: ui.finishNotification as string
|
|
30
|
+
});
|
|
22
31
|
activeTimers.push(timer);
|
|
23
32
|
|
|
24
33
|
newCard.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
@@ -39,17 +48,25 @@ function requestNotificationPermission() {
|
|
|
39
48
|
}
|
|
40
49
|
}
|
|
41
50
|
|
|
42
|
-
export function initKitchenTimers() {
|
|
51
|
+
export function initKitchenTimers(ui: Record<string, string>) {
|
|
43
52
|
const timersGrid = document.getElementById("timers-grid") as HTMLElement;
|
|
44
53
|
const timerTemplate = document.getElementById("timer-template") as HTMLTemplateElement;
|
|
45
54
|
const activeTimers: KitchenTimer[] = [];
|
|
46
55
|
|
|
47
56
|
document.querySelectorAll(".timer-card").forEach((card) => {
|
|
48
|
-
const timer = new KitchenTimer(card as HTMLElement
|
|
57
|
+
const timer = new KitchenTimer(card as HTMLElement, {
|
|
58
|
+
ready: ui.statusReady as string,
|
|
59
|
+
running: ui.statusRunning as string,
|
|
60
|
+
paused: ui.statusPaused as string,
|
|
61
|
+
finished: ui.statusFinished as string,
|
|
62
|
+
start: ui.start as string,
|
|
63
|
+
pause: ui.pause as string,
|
|
64
|
+
finishNotification: ui.finishNotification as string
|
|
65
|
+
});
|
|
49
66
|
activeTimers.push(timer);
|
|
50
67
|
});
|
|
51
68
|
|
|
52
|
-
setupAddTimerButton(timerTemplate, timersGrid, activeTimers);
|
|
69
|
+
setupAddTimerButton(timerTemplate, timersGrid, activeTimers, ui);
|
|
53
70
|
setupStopAllButton(activeTimers);
|
|
54
71
|
requestNotificationPermission();
|
|
55
72
|
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { getAudioContext, playBeep } from "./AudioHelper";
|
|
2
2
|
|
|
3
|
+
export interface TimerTranslations {
|
|
4
|
+
ready: string;
|
|
5
|
+
running: string;
|
|
6
|
+
paused: string;
|
|
7
|
+
finished: string;
|
|
8
|
+
start: string;
|
|
9
|
+
pause: string;
|
|
10
|
+
finishNotification: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
3
13
|
export class KitchenTimer extends EventTarget {
|
|
4
14
|
element: HTMLElement;
|
|
5
15
|
private inputs: { h: HTMLInputElement; m: HTMLInputElement; s: HTMLInputElement };
|
|
@@ -9,6 +19,7 @@ export class KitchenTimer extends EventTarget {
|
|
|
9
19
|
private btnAdd5m: HTMLButtonElement;
|
|
10
20
|
private statusText: HTMLElement;
|
|
11
21
|
private timerNameInput: HTMLInputElement;
|
|
22
|
+
private t: TimerTranslations;
|
|
12
23
|
|
|
13
24
|
totalSeconds: number = 0;
|
|
14
25
|
remainingSeconds: number = 0;
|
|
@@ -17,9 +28,10 @@ export class KitchenTimer extends EventTarget {
|
|
|
17
28
|
|
|
18
29
|
private audioContext: AudioContext | null = null;
|
|
19
30
|
|
|
20
|
-
constructor(element: HTMLElement) {
|
|
31
|
+
constructor(element: HTMLElement, translations: TimerTranslations) {
|
|
21
32
|
super();
|
|
22
33
|
this.element = element;
|
|
34
|
+
this.t = translations;
|
|
23
35
|
|
|
24
36
|
const hoursInput = element.querySelector(".hours") as HTMLInputElement | null;
|
|
25
37
|
const minutesInput = element.querySelector(".minutes") as HTMLInputElement | null;
|
|
@@ -153,7 +165,7 @@ export class KitchenTimer extends EventTarget {
|
|
|
153
165
|
this.setDisplay(0);
|
|
154
166
|
this.remainingSeconds = 0;
|
|
155
167
|
this.totalSeconds = 0;
|
|
156
|
-
this.statusText.textContent =
|
|
168
|
+
this.statusText.textContent = this.t.ready;
|
|
157
169
|
this.statusText.classList.remove("running", "finished");
|
|
158
170
|
this.element.classList.remove("finished");
|
|
159
171
|
this.dispatchUpdate();
|
|
@@ -173,12 +185,12 @@ export class KitchenTimer extends EventTarget {
|
|
|
173
185
|
private timeUp() {
|
|
174
186
|
this.pause();
|
|
175
187
|
this.playAlarm();
|
|
176
|
-
this.statusText.textContent =
|
|
188
|
+
this.statusText.textContent = this.t.finished;
|
|
177
189
|
this.statusText.classList.add("finished");
|
|
178
190
|
this.element.classList.add("finished");
|
|
179
191
|
|
|
180
192
|
if ("Notification" in window && Notification.permission === "granted") {
|
|
181
|
-
new Notification(
|
|
193
|
+
new Notification(`${this.t.finishNotification} ${this.getName()}`);
|
|
182
194
|
}
|
|
183
195
|
this.dispatchUpdate();
|
|
184
196
|
}
|
|
@@ -217,12 +229,12 @@ export class KitchenTimer extends EventTarget {
|
|
|
217
229
|
const btnText = this.element.querySelector(".btn-toggle .btn-text");
|
|
218
230
|
const iconPlay = this.element.querySelector(".icon-play");
|
|
219
231
|
const iconPause = this.element.querySelector(".icon-pause");
|
|
220
|
-
if (btnText) btnText.textContent =
|
|
232
|
+
if (btnText) btnText.textContent = this.t.pause;
|
|
221
233
|
iconPlay?.setAttribute("style", "display: none;");
|
|
222
234
|
iconPause?.removeAttribute("style");
|
|
223
235
|
this.element.classList.add("running");
|
|
224
236
|
this.statusText.classList.add("running");
|
|
225
|
-
this.statusText.textContent =
|
|
237
|
+
this.statusText.textContent = this.t.running;
|
|
226
238
|
Object.values(this.inputs).forEach((i) => (i.disabled = true));
|
|
227
239
|
}
|
|
228
240
|
|
|
@@ -230,12 +242,12 @@ export class KitchenTimer extends EventTarget {
|
|
|
230
242
|
const btnText = this.element.querySelector(".btn-toggle .btn-text");
|
|
231
243
|
const iconPlay = this.element.querySelector(".icon-play");
|
|
232
244
|
const iconPause = this.element.querySelector(".icon-pause");
|
|
233
|
-
if (btnText) btnText.textContent =
|
|
245
|
+
if (btnText) btnText.textContent = this.t.start;
|
|
234
246
|
iconPlay?.removeAttribute("style");
|
|
235
247
|
iconPause?.setAttribute("style", "display: none;");
|
|
236
248
|
this.element.classList.remove("running");
|
|
237
249
|
this.statusText.classList.remove("running");
|
|
238
|
-
this.statusText.textContent = this.remainingSeconds > 0 && this.remainingSeconds < this.totalSeconds ?
|
|
250
|
+
this.statusText.textContent = this.remainingSeconds > 0 && this.remainingSeconds < this.totalSeconds ? this.t.paused : this.t.ready;
|
|
239
251
|
Object.values(this.inputs).forEach((i) => (i.disabled = false));
|
|
240
252
|
}
|
|
241
253
|
|
|
@@ -334,7 +334,7 @@ const { ui } = Astro.props;
|
|
|
334
334
|
gap: 1.5rem;
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
-
.meringue-result-item {
|
|
337
|
+
:global(.meringue-result-item) {
|
|
338
338
|
background: var(--time-bg);
|
|
339
339
|
border: 1px solid var(--card-border);
|
|
340
340
|
border-radius: 20px;
|
|
@@ -345,7 +345,7 @@ const { ui } = Astro.props;
|
|
|
345
345
|
transition: all 0.2s ease;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
.meringue-result-item:hover {
|
|
348
|
+
:global(.meringue-result-item):hover {
|
|
349
349
|
background: var(--card-bg);
|
|
350
350
|
transform: scale(1.02);
|
|
351
351
|
}
|
|
@@ -384,7 +384,7 @@ const { ui } = Astro.props;
|
|
|
384
384
|
gap: 1rem;
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
-
.meringue-time-row {
|
|
387
|
+
:global(.meringue-time-row) {
|
|
388
388
|
display: flex;
|
|
389
389
|
align-items: center;
|
|
390
390
|
justify-content: space-between;
|
|
@@ -450,7 +450,7 @@ const { ui } = Astro.props;
|
|
|
450
450
|
color: var(--pink-light);
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
.meringue-fade-in {
|
|
453
|
+
:global(.meringue-fade-in) {
|
|
454
454
|
animation: meringue-fade-in 0.5s ease forwards;
|
|
455
455
|
}
|
|
456
456
|
|
|
@@ -262,7 +262,7 @@ export const content: ToolLocaleContent = {
|
|
|
262
262
|
{
|
|
263
263
|
type: "diagnostic",
|
|
264
264
|
variant: "warning",
|
|
265
|
-
title: "Votre Meringue rejette
|
|
265
|
+
title: "Votre Meringue rejette t elle du Liquide ?",
|
|
266
266
|
html: "Si vous voyez du sirop s'échapper (synérèse), c'est soit que le sucre n'était pas dissous, soit que l'humidité ambiante est trop forte. Pour l'italienne, versez le sirop en filet constant, jamais directement sur le fouet.",
|
|
267
267
|
},
|
|
268
268
|
{
|
|
@@ -274,13 +274,13 @@ const { ui } = Astro.props;
|
|
|
274
274
|
width: 2.5rem;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
-
.ms-input-group {
|
|
277
|
+
:global(.ms-input-group) {
|
|
278
278
|
display: flex;
|
|
279
279
|
flex-direction: column;
|
|
280
280
|
gap: 0.5rem;
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
-
.ms-label {
|
|
283
|
+
:global(.ms-label) {
|
|
284
284
|
font-size: 0.75rem;
|
|
285
285
|
font-weight: 700;
|
|
286
286
|
color: var(--ms-text-muted);
|
|
@@ -288,7 +288,7 @@ const { ui } = Astro.props;
|
|
|
288
288
|
letter-spacing: 0.05em;
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
.ms-input {
|
|
291
|
+
:global(.ms-input) {
|
|
292
292
|
width: 100%;
|
|
293
293
|
padding: 0.75rem 1rem;
|
|
294
294
|
border-radius: 0.5rem;
|
|
@@ -299,7 +299,7 @@ const { ui } = Astro.props;
|
|
|
299
299
|
transition: var(--ms-transition);
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
.ms-input:focus {
|
|
302
|
+
:global(.ms-input):focus {
|
|
303
303
|
outline: none;
|
|
304
304
|
border-color: var(--ms-primary);
|
|
305
305
|
box-shadow: 0 0 0 3px hsla(262deg, 83%, 58%, 0.1);
|
|
@@ -309,7 +309,7 @@ const { ui } = Astro.props;
|
|
|
309
309
|
text-align: center;
|
|
310
310
|
padding: 2.5rem;
|
|
311
311
|
background: linear-gradient(135deg, var(--ms-primary), var(--ms-primary-dark));
|
|
312
|
-
color:
|
|
312
|
+
color: hsl(0deg, 0%, 100%);
|
|
313
313
|
border-radius: var(--ms-radius);
|
|
314
314
|
box-shadow: var(--ms-shadow-md);
|
|
315
315
|
position: relative;
|
|
@@ -402,7 +402,7 @@ const { ui } = Astro.props;
|
|
|
402
402
|
margin-bottom: 1.5rem;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
.ms-ingredient-row {
|
|
405
|
+
:global(.ms-ingredient-row) {
|
|
406
406
|
display: grid;
|
|
407
407
|
grid-template-columns: 1fr;
|
|
408
408
|
gap: 0.75rem;
|
|
@@ -426,13 +426,13 @@ const { ui } = Astro.props;
|
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
@media (min-width: 640px) {
|
|
429
|
-
.ms-ingredient-row {
|
|
429
|
+
:global(.ms-ingredient-row) {
|
|
430
430
|
grid-template-columns: 2fr 1fr 1fr auto;
|
|
431
431
|
align-items: center;
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
-
.ms-ingredient-final {
|
|
435
|
+
:global(.ms-ingredient-final) {
|
|
436
436
|
font-weight: 800;
|
|
437
437
|
color: hsl(150deg, 80%, 40%);
|
|
438
438
|
font-size: 1.125rem;
|
|
@@ -440,7 +440,7 @@ const { ui } = Astro.props;
|
|
|
440
440
|
text-align: center;
|
|
441
441
|
}
|
|
442
442
|
|
|
443
|
-
.ms-del-btn {
|
|
443
|
+
:global(.ms-del-btn) {
|
|
444
444
|
background: transparent;
|
|
445
445
|
border: none;
|
|
446
446
|
color: var(--ms-text-muted);
|
|
@@ -450,7 +450,7 @@ const { ui } = Astro.props;
|
|
|
450
450
|
transition: var(--ms-transition);
|
|
451
451
|
}
|
|
452
452
|
|
|
453
|
-
.ms-del-btn:hover {
|
|
453
|
+
:global(.ms-del-btn):hover {
|
|
454
454
|
background: hsl(0deg, 85%, 60%);
|
|
455
455
|
color: var(--ms-bg-card);
|
|
456
456
|
}
|
|
@@ -476,11 +476,17 @@ const { ui } = Astro.props;
|
|
|
476
476
|
border-style: solid;
|
|
477
477
|
}
|
|
478
478
|
|
|
479
|
-
.ms-empty-state {
|
|
479
|
+
:global(.ms-empty-state) {
|
|
480
480
|
text-align: center;
|
|
481
481
|
padding: 2rem;
|
|
482
482
|
color: var(--ms-text-muted);
|
|
483
483
|
font-style: italic;
|
|
484
484
|
}
|
|
485
|
+
|
|
486
|
+
:global(.ms-inputs-grid) {
|
|
487
|
+
display: grid;
|
|
488
|
+
grid-template-columns: 1fr 1fr;
|
|
489
|
+
gap: 1rem;
|
|
490
|
+
}
|
|
485
491
|
</style>
|
|
486
492
|
|