5htp-core 0.4.2 → 0.4.3
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 +4 -12
- package/src/client/app/component.tsx +11 -2
- package/src/client/assets/css/components/button.less +10 -10
- package/src/client/assets/css/components/other.less +0 -1
- package/src/client/assets/css/core.less +0 -4
- package/src/client/assets/css/text/text.less +1 -2
- package/src/client/assets/css/text/titres.less +0 -5
- package/src/client/assets/css/utils/layouts.less +5 -5
- package/src/client/assets/css/utils/sizing.less +6 -6
- package/src/client/components/Dialog/card.tsx +1 -1
- package/src/client/components/button.tsx +1 -1
- package/src/client/components/containers/Popover/index.tsx +2 -2
- package/src/client/components/index.ts +5 -16
- package/src/client/components/inputv3/index.tsx +2 -5
- package/src/client/services/router/components/Page.tsx +1 -2
- package/src/client/services/router/components/router.tsx +4 -8
- package/src/client/services/router/request/api.ts +31 -52
- package/src/client/services/router/response/index.tsx +2 -0
- package/src/common/data/dates.ts +50 -6
- package/src/common/router/layouts.ts +14 -5
- package/src/common/router/response/page.ts +12 -6
- package/src/common/validation/validators.ts +0 -4
- package/src/server/app/container/config.ts +2 -1
- package/src/server/services/router/http/index.ts +16 -22
- package/src/server/services/router/response/page/index.tsx +0 -0
- package/src/client/assets/fonts/Inter/index.less +0 -77
- package/src/client/assets/fonts/Inter/latin-500.woff2 +0 -0
- package/src/client/assets/fonts/Inter/latin-600.woff2 +0 -0
- package/src/client/assets/fonts/Inter/latin-800.woff2 +0 -0
- package/src/client/assets/fonts/Inter/latin-ext-500.woff2 +0 -0
- package/src/client/assets/fonts/Inter/latin-ext-600.woff2 +0 -0
- package/src/client/assets/fonts/Inter/latin-ext-800.woff2 +0 -0
- package/src/client/assets/fonts/Rubik/cyrillic-ext.woff2 +0 -0
- package/src/client/assets/fonts/Rubik/cyrillic.woff2 +0 -0
- package/src/client/assets/fonts/Rubik/hebrew.woff2 +0 -0
- package/src/client/assets/fonts/Rubik/index.less +0 -30
- package/src/client/assets/fonts/Rubik/latin-ext.woff2 +0 -0
- package/src/client/assets/fonts/Rubik/latin.woff2 +0 -0
- package/src/client/components/Amount.tsx +0 -38
- package/src/client/components/Form_old/index.tsx +0 -450
- package/src/client/components/Form_old/index.tsx.old +0 -436
- package/src/client/components/dropdown.old/Manager.tsx +0 -164
- package/src/client/components/dropdown.old/getPosition.ts +0 -137
- package/src/client/components/dropdown.old/index.tsx +0 -99
- package/src/client/components/dropdown.old/popover.less +0 -56
- package/src/client/components/input/Base/Choix.ts +0 -48
- package/src/client/components/input/Base/index.tsx +0 -432
- package/src/client/components/input/BaseV2/index.tsx +0 -72
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { ComponentChild } from 'preact';
|
|
8
|
-
import dottie from 'dottie';
|
|
9
|
-
|
|
10
|
-
// Libs
|
|
11
|
-
import { TListeErreursSaisie, ErreurSaisieSchema } from '@erreurs';
|
|
12
|
-
import { TPropsChamp as PropsChamp, propsDefautChampForm } from '@client/components/Champs/Base';
|
|
13
|
-
import { Schema, TBaseComposantChamp, TObjetDonnees, TRetourValidation } from '@commun/donnees/schema';
|
|
14
|
-
import { simpleDeepCopy } from '@commun/donnees/objets';
|
|
15
|
-
import { ContexteOnglets } from '@client/components/Conteneurs/Onglets';
|
|
16
|
-
|
|
17
|
-
/*----------------------------------
|
|
18
|
-
- TYPES ENTREE
|
|
19
|
-
----------------------------------*/
|
|
20
|
-
|
|
21
|
-
export type TComposantChamps<TDonnees> = { [nomChamp in keyof TDonnees]: ComponentChild };
|
|
22
|
-
|
|
23
|
-
type TPropsHook<TDonnees extends TObjetDonnees> = {
|
|
24
|
-
|
|
25
|
-
// Informations générales
|
|
26
|
-
nom: string,
|
|
27
|
-
donnees?: TDonnees,
|
|
28
|
-
schema: Schema<TDonnees>,
|
|
29
|
-
|
|
30
|
-
// Présentation & état
|
|
31
|
-
progression?: false | number,
|
|
32
|
-
autosave?: boolean,
|
|
33
|
-
readOnly?: boolean,
|
|
34
|
-
|
|
35
|
-
// Actions
|
|
36
|
-
onChange?: (donnees: TDonnees, validation: TRetourValidation<TDonnees> | false) => void,
|
|
37
|
-
afterValidation?: (validation: TRetourValidation<TDonnees>) => void,
|
|
38
|
-
envoyer?: (donnees: TDonnees) => Promise<void>,
|
|
39
|
-
refActions?: (actionsForm: TActionsForm<TDonnees>) => void,
|
|
40
|
-
onSet?: { [nomChamp: string]: (anciennesDonnees: TDonnees, nouvellesDonnees: TDonnees) => TDonnees }
|
|
41
|
-
|
|
42
|
-
// Champs
|
|
43
|
-
propsChamps?: { [cle: string]: any }
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/*----------------------------------
|
|
48
|
-
- TYPES SORTIE
|
|
49
|
-
----------------------------------*/
|
|
50
|
-
export type TEtatForm<TDonnees extends TObjetDonnees> = {
|
|
51
|
-
saisie: Partial<TDonnees>,
|
|
52
|
-
Champs: any,
|
|
53
|
-
form: TActionsForm<TDonnees>,
|
|
54
|
-
schema: Schema<TDonnees>,
|
|
55
|
-
changes: number
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type TActionsForm<TDonnees> = {
|
|
59
|
-
envoyer: (e?: any) => Promise<void>,
|
|
60
|
-
set: (valeurs: Partial<TDonnees>) => void,
|
|
61
|
-
get: (champ?: string) => any | TDonnees,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/*----------------------------------
|
|
65
|
-
- COMPOSANTS
|
|
66
|
-
----------------------------------*/
|
|
67
|
-
const debug = true;
|
|
68
|
-
export const useForm = <TDonnees extends TObjetDonnees>(props: TPropsHook<TDonnees>): TEtatForm<TDonnees> => {
|
|
69
|
-
|
|
70
|
-
const onglets = React.useContext(ContexteOnglets);
|
|
71
|
-
const refChamps = React.useRef<{ [nomChamp: string]: HTMLElement }>({});
|
|
72
|
-
|
|
73
|
-
/*----------------------------------
|
|
74
|
-
- INIT
|
|
75
|
-
----------------------------------*/
|
|
76
|
-
|
|
77
|
-
// State
|
|
78
|
-
const [state, setState] = React.useState<{
|
|
79
|
-
donnees: Partial<TDonnees>,
|
|
80
|
-
erreurs: TListeErreursSaisie,
|
|
81
|
-
errorsCount: number,
|
|
82
|
-
progression: false | number,
|
|
83
|
-
changed: Partial<TDonnees> // Nom des champs changés depuis le dernier enregistrement
|
|
84
|
-
}>({
|
|
85
|
-
donnees: props.donnees || {},
|
|
86
|
-
erreurs: {},
|
|
87
|
-
errorsCount: 0,
|
|
88
|
-
progression: false,
|
|
89
|
-
changed: {}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const donneesExternes = props.donnees !== undefined && props.onChange !== undefined;
|
|
93
|
-
const donneesInit = ((donneesExternes ? props.donnees : state.donnees) || {}) as Partial<TDonnees>
|
|
94
|
-
const schema = props.schema.generer(donneesInit);
|
|
95
|
-
const donnees = schema.initDonnees(donneesInit, true);
|
|
96
|
-
|
|
97
|
-
/*----------------------------------
|
|
98
|
-
- METHODES
|
|
99
|
-
----------------------------------*/
|
|
100
|
-
function get(champ: string): any;
|
|
101
|
-
function get(): TDonnees;
|
|
102
|
-
function get(champ?: string): any | TDonnees {
|
|
103
|
-
return champ === undefined
|
|
104
|
-
? donnees
|
|
105
|
-
: donnees[champ];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
async function onChange(
|
|
109
|
-
valeursInit: Partial<TDonnees>,
|
|
110
|
-
newState: Partial<typeof state> = {},
|
|
111
|
-
validerMaintenant: boolean = true
|
|
112
|
-
): Promise<{ erreurs: TListeErreursSaisie, errorsCount: number }> {
|
|
113
|
-
|
|
114
|
-
// RAPPEL: donnees = anciennes données
|
|
115
|
-
|
|
116
|
-
if (debug) console.log(`[form][saisie] onChange`, valeursInit);
|
|
117
|
-
|
|
118
|
-
// Validation des données changées
|
|
119
|
-
// TODO: conserver erreurs des champs n'ayant pas été changés
|
|
120
|
-
let nouvellesDonnees: Partial<TDonnees>;
|
|
121
|
-
let errorsCount: number = 0;
|
|
122
|
-
let erreurs: TListeErreursSaisie = {}
|
|
123
|
-
if (validerMaintenant) {
|
|
124
|
-
|
|
125
|
-
({
|
|
126
|
-
valeurs: nouvellesDonnees,
|
|
127
|
-
errorsCount,
|
|
128
|
-
erreurs
|
|
129
|
-
} = await valider(valeursInit));
|
|
130
|
-
|
|
131
|
-
if (debug && errorsCount !== 0) console.log(`[form][saisie] erreurs`, erreurs);
|
|
132
|
-
|
|
133
|
-
newState.erreurs = erreurs;
|
|
134
|
-
newState.errorsCount = errorsCount;
|
|
135
|
-
|
|
136
|
-
} else
|
|
137
|
-
nouvellesDonnees = valeursInit;
|
|
138
|
-
|
|
139
|
-
// Validation & mapping personnalisé
|
|
140
|
-
/*if (props.filtres?.after)
|
|
141
|
-
nouvellesDonnees = props.filtres.after(nouvellesDonnees, donnees);*/
|
|
142
|
-
|
|
143
|
-
// Copie + fusion sans références
|
|
144
|
-
const donneesCompletes = simpleDeepCopy(donnees, nouvellesDonnees);
|
|
145
|
-
|
|
146
|
-
// Màj valeur + erreurs
|
|
147
|
-
// Le passage d'une fonction permet de récupérer le dernier state, ce qui évite à composantChamps de redevenir undefined
|
|
148
|
-
setState((stateA) => ({
|
|
149
|
-
...stateA,
|
|
150
|
-
donnees: donneesCompletes,
|
|
151
|
-
changed: { ...stateA.changed, ...nouvellesDonnees },
|
|
152
|
-
...newState
|
|
153
|
-
}));
|
|
154
|
-
|
|
155
|
-
if (props.onChange && errorsCount === 0)
|
|
156
|
-
props.onChange(donneesCompletes, validerMaintenant ? { valeurs: nouvellesDonnees, errorsCount, erreurs } : false);
|
|
157
|
-
|
|
158
|
-
/*if (valider && props.autosave === true) {
|
|
159
|
-
|
|
160
|
-
if (props.envoyer === undefined)
|
|
161
|
-
throw new Error("autosave = true, mais aucune fonction d'enregistrement n'est passée dans props.envoyer");
|
|
162
|
-
|
|
163
|
-
await envoyer();
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}*/
|
|
167
|
-
|
|
168
|
-
return { erreurs, errorsCount };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async function valider(
|
|
172
|
-
donneesAvalider: Partial<TDonnees>
|
|
173
|
-
): Promise<TRetourValidation<TDonnees>> {
|
|
174
|
-
|
|
175
|
-
console.log(`[form][saisie] Valider`, donneesAvalider, 'Données complètes =', donnees);
|
|
176
|
-
|
|
177
|
-
// Pas de valeur pécifiées = on valide toutes les données du form
|
|
178
|
-
const retour = await schema.valider(donneesAvalider, donnees, undefined, {
|
|
179
|
-
corriger: true
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Focus sur le premier champ ayant déclenché une erreur
|
|
183
|
-
if (retour.errorsCount !== 0) {
|
|
184
|
-
|
|
185
|
-
const cheminChamp = Object.keys(retour.erreurs)[0]
|
|
186
|
-
const champ = schema.get(cheminChamp);
|
|
187
|
-
|
|
188
|
-
// Navig onglet
|
|
189
|
-
if (onglets !== null) {
|
|
190
|
-
onglets.navig(champ.onglet);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Scroll
|
|
194
|
-
const elemChamp = refChamps.current[cheminChamp];
|
|
195
|
-
elemChamp?.scrollIntoView({ behavior: 'smooth', block: 'start', inline: 'start' })
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (props.afterValidation !== undefined)
|
|
201
|
-
props.afterValidation(retour, actions);
|
|
202
|
-
|
|
203
|
-
return retour;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function envoyer(e?: any): Promise<boolean> {
|
|
207
|
-
|
|
208
|
-
if (e !== undefined)
|
|
209
|
-
e.preventDefault();
|
|
210
|
-
|
|
211
|
-
if (props.readOnly)
|
|
212
|
-
return false;
|
|
213
|
-
|
|
214
|
-
// Déjà en cours d'envoi
|
|
215
|
-
if (props.progression !== undefined && props.progression !== false)
|
|
216
|
-
return false;
|
|
217
|
-
|
|
218
|
-
if (state.errorsCount !== 0)
|
|
219
|
-
return false;
|
|
220
|
-
|
|
221
|
-
console.log(`[form][saisie] Envoyer`, donnees);
|
|
222
|
-
|
|
223
|
-
// Validation de l'ensemble des champs
|
|
224
|
-
const { erreurs, errorsCount, valeurs } = await valider(donnees);
|
|
225
|
-
if (errorsCount !== 0) {
|
|
226
|
-
setState((stateA) => ({ ...stateA, erreurs, errorsCount }));
|
|
227
|
-
console.error('Erreurs formulaire', erreurs);
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
console.log("@@@ Envoi formulaire 2", valeurs);
|
|
232
|
-
|
|
233
|
-
// Envoi
|
|
234
|
-
if (props.envoyer) {
|
|
235
|
-
|
|
236
|
-
if (!('progression' in props))
|
|
237
|
-
setState((stateA) => ({ ...stateA, progression: 0 }));
|
|
238
|
-
|
|
239
|
-
//Ayant été validée, on estime que les données sont complètes
|
|
240
|
-
const envoi = props.envoyer(valeurs as TDonnees);
|
|
241
|
-
|
|
242
|
-
if (envoi.then === undefined)
|
|
243
|
-
throw new Error("La fonction passée dans props.envoyer doit retourner une promise.");
|
|
244
|
-
|
|
245
|
-
return await envoi.then(() => {
|
|
246
|
-
|
|
247
|
-
setState((stateA) => ({
|
|
248
|
-
...stateA,
|
|
249
|
-
progression: ('progression' in props)
|
|
250
|
-
? stateA.progression
|
|
251
|
-
: false,
|
|
252
|
-
changed: {} // Reset de la liste des données changées
|
|
253
|
-
}));
|
|
254
|
-
|
|
255
|
-
return true;
|
|
256
|
-
|
|
257
|
-
}).catch((e) => {
|
|
258
|
-
|
|
259
|
-
if (debug) console.log("RETOUR ERREURS API", e);
|
|
260
|
-
|
|
261
|
-
if (e instanceof ErreurSaisieSchema) {
|
|
262
|
-
|
|
263
|
-
setState((stateA) => ({
|
|
264
|
-
...stateA,
|
|
265
|
-
erreurs: e.erreursSaisie,
|
|
266
|
-
progression: 'progression' in props ? stateA.progression : false
|
|
267
|
-
}));
|
|
268
|
-
|
|
269
|
-
} else
|
|
270
|
-
throw e;
|
|
271
|
-
|
|
272
|
-
return false;
|
|
273
|
-
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/*----------------------------------
|
|
281
|
-
- RENDU CHAMPS
|
|
282
|
-
----------------------------------*/
|
|
283
|
-
const rendreChamps = (schema: Schema<{}>, donnees: TObjetDonnees) => {
|
|
284
|
-
|
|
285
|
-
const schemaPourRendu: {
|
|
286
|
-
[champ: string]: {
|
|
287
|
-
composant: TBaseComposantChamp,
|
|
288
|
-
attrs: (propsBase: PropsChamp<unknown>) => PropsChamp<unknown>
|
|
289
|
-
}
|
|
290
|
-
} = {}
|
|
291
|
-
|
|
292
|
-
for (const nomChamp in schema.champs) {
|
|
293
|
-
|
|
294
|
-
// Désactivé = pas d'affichage
|
|
295
|
-
if (schema.champs[nomChamp].activer === false)
|
|
296
|
-
continue;
|
|
297
|
-
|
|
298
|
-
// Sous-schema
|
|
299
|
-
if (schema.champs[nomChamp] instanceof Schema) {
|
|
300
|
-
schemaPourRendu[nomChamp] = rendreChamps(schema.champs[nomChamp], donnees[nomChamp] || {});
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const { rendu, as, ...attrs } = schema.champs[nomChamp];
|
|
305
|
-
|
|
306
|
-
// Prérendu
|
|
307
|
-
if (rendu === undefined)
|
|
308
|
-
continue;
|
|
309
|
-
|
|
310
|
-
// Correction valeur selon as
|
|
311
|
-
const valeur = as === undefined
|
|
312
|
-
? donnees[nomChamp]
|
|
313
|
-
// Champ composé de plusieurs valeurs
|
|
314
|
-
: as.map((nomDonnee) => donnees[nomDonnee]);
|
|
315
|
-
|
|
316
|
-
/*
|
|
317
|
-
ATTENTION: Faire en sorte que les propriétés passées dans le composant soient bien
|
|
318
|
-
fusionnées avec les prorpiétés injectées par le form. Ex: onChange, attrsChamp, ...
|
|
319
|
-
*/
|
|
320
|
-
|
|
321
|
-
const cheminChamp = [...schema.chemin, nomChamp].join('.');
|
|
322
|
-
|
|
323
|
-
schemaPourRendu[nomChamp] = {
|
|
324
|
-
composant: rendu,
|
|
325
|
-
attrs: (propsChamp) => ({
|
|
326
|
-
|
|
327
|
-
// Config par défaut pour les champs appartenant au form (ex: label systèmatique)
|
|
328
|
-
...propsDefautChampForm,
|
|
329
|
-
|
|
330
|
-
// Attributs définis via le schema
|
|
331
|
-
...(attrs || {}),
|
|
332
|
-
// Attributs définis via l'appel du composant
|
|
333
|
-
...(propsChamp || {}),
|
|
334
|
-
|
|
335
|
-
// Rattachement au form
|
|
336
|
-
ref: (ref: HTMLElement) => refChamps.current[cheminChamp] = ref?.base,
|
|
337
|
-
nom: nomChamp,
|
|
338
|
-
valeur: valeur,
|
|
339
|
-
as: as,
|
|
340
|
-
valider: undefined, // Pas de validation dans le champ. On fait tous les champs em même temps depuis le form
|
|
341
|
-
erreurs: state.erreurs[cheminChamp],
|
|
342
|
-
readOnly: props.readOnly, // Si le form est readonly, alors tous ses champs le sont obligatoirement
|
|
343
|
-
onChange: async (nouvelleValeur: any, validerMaintenant: boolean = false) => {
|
|
344
|
-
|
|
345
|
-
// Aucun changement
|
|
346
|
-
// NOTE: Quand executé depuis onFocus, la valeur a déjà été changée via onChange
|
|
347
|
-
if (nouvelleValeur === valeur && !validerMaintenant)
|
|
348
|
-
return;
|
|
349
|
-
|
|
350
|
-
// Mapping données
|
|
351
|
-
let nouvellesDonnees: Partial<TDonnees>;
|
|
352
|
-
if (as !== undefined) {
|
|
353
|
-
nouvellesDonnees = dottie.transform({
|
|
354
|
-
[ [...schema.chemin, as[0]].join('.') ]: nouvelleValeur[0],
|
|
355
|
-
[ [...schema.chemin, as[1]].join('.') ]: nouvelleValeur[1]
|
|
356
|
-
});
|
|
357
|
-
// Mapping classique
|
|
358
|
-
} else
|
|
359
|
-
nouvellesDonnees = dottie.transform({
|
|
360
|
-
[cheminChamp]: nouvelleValeur
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Application des changements
|
|
364
|
-
const { errorsCount } = await onChange(nouvellesDonnees, {}, validerMaintenant);
|
|
365
|
-
|
|
366
|
-
// Si aucune erreur, onChange propre au champ + gestion erreurs
|
|
367
|
-
if (propsChamp.onChange !== undefined && errorsCount === 0)
|
|
368
|
-
await propsChamp.onChange(nouvelleValeur, true).catch((e) => {
|
|
369
|
-
if (validerMaintenant)
|
|
370
|
-
setState((stateA) => ({
|
|
371
|
-
...stateA,
|
|
372
|
-
erreurs: {
|
|
373
|
-
...stateA.erreurs,
|
|
374
|
-
[cheminChamp]: [e]
|
|
375
|
-
}
|
|
376
|
-
}));
|
|
377
|
-
})
|
|
378
|
-
},
|
|
379
|
-
})
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return schemaPourRendu;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
const champs = rendreChamps(schema, donnees);
|
|
387
|
-
|
|
388
|
-
/*----------------------------------
|
|
389
|
-
- RETOUR
|
|
390
|
-
----------------------------------*/
|
|
391
|
-
const actions = {
|
|
392
|
-
form: {
|
|
393
|
-
envoyer,
|
|
394
|
-
get,
|
|
395
|
-
set: onChange,
|
|
396
|
-
},
|
|
397
|
-
saisie: donnees,
|
|
398
|
-
Champs: champs,
|
|
399
|
-
schema,
|
|
400
|
-
changes: Object.keys(state.changed).length
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return actions;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
export default <TDonnees extends TObjetDonnees>({ children, className, style, ...optsHook }: {
|
|
407
|
-
|
|
408
|
-
// Champs
|
|
409
|
-
children: ComponentChild | ((
|
|
410
|
-
form: TActionsForm<TDonnees>,
|
|
411
|
-
donnees: Partial<TDonnees>,
|
|
412
|
-
champs: TComposantChamps<TDonnees>
|
|
413
|
-
) => ComponentChild),
|
|
414
|
-
|
|
415
|
-
// Présentation
|
|
416
|
-
className?: string,
|
|
417
|
-
style?: any,
|
|
418
|
-
|
|
419
|
-
} & TPropsHook<TDonnees>) => {
|
|
420
|
-
|
|
421
|
-
if (typeof children === 'function') {
|
|
422
|
-
const { form, donnees, Champs } = useForm(optsHook);
|
|
423
|
-
children = children(form, donnees, Champs);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return (
|
|
427
|
-
<form className={className} style={style}/*onSubmit={envoyer}*/>
|
|
428
|
-
{children}
|
|
429
|
-
</form>
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
/*
|
|
433
|
-
<Chargement progression={'progression' in props ? props.progression : state.progression}>
|
|
434
|
-
</Chargement>
|
|
435
|
-
*/
|
|
436
|
-
}
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/*----------------------------------
|
|
2
|
-
- DEPENDANCES
|
|
3
|
-
----------------------------------*/
|
|
4
|
-
|
|
5
|
-
// Npm
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import { ComponentChild, JSX } from 'preact';
|
|
8
|
-
|
|
9
|
-
// Libs
|
|
10
|
-
import useContexte from '@client/context';
|
|
11
|
-
import getPosition, { TSide, TPosition } from './getPosition';
|
|
12
|
-
import { blurable, deepContains } from '@client/utils/dom';
|
|
13
|
-
|
|
14
|
-
/*----------------------------------
|
|
15
|
-
- TYPES: IMPORTATIONS
|
|
16
|
-
----------------------------------*/
|
|
17
|
-
|
|
18
|
-
/*----------------------------------
|
|
19
|
-
- TYPES: DECLARATIONS
|
|
20
|
-
----------------------------------*/
|
|
21
|
-
|
|
22
|
-
export type PopoverProps = {
|
|
23
|
-
|
|
24
|
-
origin: HTMLElement,
|
|
25
|
-
content: JSX.Element,
|
|
26
|
-
interactions?: boolean
|
|
27
|
-
position?: TSide,
|
|
28
|
-
frame?: React.Ref<HTMLDivElement>,
|
|
29
|
-
|
|
30
|
-
onVisibilityChange?: (visible: boolean) => void,
|
|
31
|
-
close: () => void,
|
|
32
|
-
initialPosition: TPosition,
|
|
33
|
-
|
|
34
|
-
immutable?: boolean, // If true, don't add classes, nor childrens
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export type TPopover = PopoverProps & {
|
|
38
|
-
id: number,
|
|
39
|
-
close: () => void,
|
|
40
|
-
visible: boolean,
|
|
41
|
-
onVisibilityChange?: (visible: boolean) => void,
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const Popover = ({ id, origin, content, interactions, position: side, frame, close, initialPosition, onVisibilityChange }: TPopover) => {
|
|
45
|
-
|
|
46
|
-
const [position, setPosition] = React.useState<TPosition>(initialPosition);
|
|
47
|
-
const refElement = React.createRef<HTMLElement>();
|
|
48
|
-
|
|
49
|
-
React.useEffect(() => {
|
|
50
|
-
|
|
51
|
-
if (!refElement.current)
|
|
52
|
-
return console.warn("Unable to get the popover dom origin to compute the correct position");
|
|
53
|
-
|
|
54
|
-
// Now the popover is shown and we know his dipensions,
|
|
55
|
-
// we can set the correct position
|
|
56
|
-
setPosition(
|
|
57
|
-
getPosition(
|
|
58
|
-
origin,
|
|
59
|
-
refElement.current,
|
|
60
|
-
side,
|
|
61
|
-
frame?.current || (document.getElementById('#layout') as HTMLElement)
|
|
62
|
-
)
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
}, [id]);
|
|
66
|
-
|
|
67
|
-
return React.cloneElement(content, {
|
|
68
|
-
className: (content.props.className || '') + ' popover' + (position ? ' pos_' + position.cote : ''),
|
|
69
|
-
onClick: () => {
|
|
70
|
-
if (!interactions) {
|
|
71
|
-
close();
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
ref: refElement,
|
|
75
|
-
style: {
|
|
76
|
-
...(content.props.style || {}),
|
|
77
|
-
...(position ? {
|
|
78
|
-
top: position.css.top,
|
|
79
|
-
left: position.css.left,
|
|
80
|
-
right: position.css.right,
|
|
81
|
-
bottom: position.css.bottom,
|
|
82
|
-
} : {}),
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/*----------------------------------
|
|
89
|
-
- COMPOSANT
|
|
90
|
-
----------------------------------*/
|
|
91
|
-
let curId: number = 0;
|
|
92
|
-
export default ({ }: {
|
|
93
|
-
|
|
94
|
-
}) => {
|
|
95
|
-
|
|
96
|
-
const ctx = useContexte();
|
|
97
|
-
|
|
98
|
-
const [popover, setPopover] = React.useState<TPopover | null>(null);
|
|
99
|
-
|
|
100
|
-
ctx.popover = (props: PopoverProps): TPopover => {
|
|
101
|
-
|
|
102
|
-
const id = curId++;
|
|
103
|
-
const close = () => {
|
|
104
|
-
setPopover(p => p && p.id === id ? null : p);
|
|
105
|
-
if (props.onVisibilityChange)
|
|
106
|
-
props.onVisibilityChange(false);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const popover = {
|
|
110
|
-
...props,
|
|
111
|
-
|
|
112
|
-
id,
|
|
113
|
-
visible: true,
|
|
114
|
-
close
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
if (props.onVisibilityChange)
|
|
118
|
-
props.onVisibilityChange(true);
|
|
119
|
-
|
|
120
|
-
setPopover(existing => {
|
|
121
|
-
|
|
122
|
-
if (existing !== null && existing.onVisibilityChange)
|
|
123
|
-
existing.onVisibilityChange(false);
|
|
124
|
-
|
|
125
|
-
return popover;
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
return popover;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (popover === null)
|
|
133
|
-
return null;
|
|
134
|
-
|
|
135
|
-
React.useEffect(() => {
|
|
136
|
-
const blur = (e: MouseEvent) => {
|
|
137
|
-
|
|
138
|
-
const clickedOnPopover = deepContains([".popover", '.btn.dropdown'], e.target);
|
|
139
|
-
if (clickedOnPopover)
|
|
140
|
-
return;
|
|
141
|
-
|
|
142
|
-
if (popover.onVisibilityChange)
|
|
143
|
-
popover.onVisibilityChange(false);
|
|
144
|
-
|
|
145
|
-
setPopover(null);
|
|
146
|
-
};
|
|
147
|
-
document.addEventListener('mousedown', blur);
|
|
148
|
-
return () => document.removeEventListener('mousedown', blur);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const initialPosition = getPosition(
|
|
152
|
-
popover.origin,
|
|
153
|
-
[200 /* = min-width .popover */, 100],
|
|
154
|
-
popover.position,
|
|
155
|
-
popover.frame?.current || (document.getElementById('#layout') as HTMLElement)
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
return (
|
|
159
|
-
<div id="popover">
|
|
160
|
-
<Popover {...popover} initialPosition={initialPosition} />
|
|
161
|
-
</div>
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
}
|