@rsuci/shared-form-components 1.0.17 → 1.0.19

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.
@@ -41,14 +41,32 @@ export declare class RosterConditionEngine {
41
41
  */
42
42
  static extractVariableReferences(condition: string): string[];
43
43
  /**
44
- * Extrait la cible d'un jump s'il existe dans la condition
44
+ * Extrait la cible d'un jump s'il existe dans la condition (premier match uniquement)
45
45
  * Format: jump(condition, ${TARGET})
46
+ * @deprecated Utiliser extractAllJumps pour supporter les jumps multiples
46
47
  */
47
48
  static extractJumpTarget(condition: string): string | null;
49
+ /**
50
+ * Extrait TOUS les jumps d'une condition
51
+ * Supporte: jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
52
+ * Retourne un tableau de { fullExpression, innerCondition, target }
53
+ */
54
+ static extractAllJumps(condition: string): Array<{
55
+ fullExpression: string;
56
+ innerCondition: string;
57
+ target: string;
58
+ }>;
48
59
  /**
49
60
  * Calcule les variables qui doivent être masquées à cause des jumps actifs
61
+ * Supporte les jumps multiples avec court-circuit:
62
+ * - jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
63
+ * - Si cond1 est vraie, on saute à VAR1 et cond2 n'est pas évaluée
50
64
  */
51
65
  static computeJumpedVariables(sortedVariables: RosterVariableRef[], lineValues: Record<string, any>): Set<string>;
66
+ /**
67
+ * Évalue une condition interne (sans le wrapper jump())
68
+ */
69
+ private static evaluateInnerCondition;
52
70
  /**
53
71
  * Valide la syntaxe de base d'une condition
54
72
  */
@@ -1 +1 @@
1
- {"version":3,"file":"roster-condition-engine.d.ts","sourceRoot":"","sources":["../../src/lib/roster-condition-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,wBAAwB,GAChC,uBAAuB,GACvB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,cAAc,CAAC;AAEnB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAEhC;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CACtB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,iBAAiB,EAAE,EACvC,mBAAmB,EAAE,MAAM,EAC3B,oBAAoB,EAAE,MAAM,GAC3B,+BAA+B;IAgElC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAyB5E;;;OAGG;IACH,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAO7D;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAO1D;;OAEG;IACH,MAAM,CAAC,sBAAsB,CAC3B,eAAe,EAAE,iBAAiB,EAAE,EACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,GAAG,CAAC,MAAM,CAAC;IA2Bd;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;CA0B9B"}
1
+ {"version":3,"file":"roster-condition-engine.d.ts","sourceRoot":"","sources":["../../src/lib/roster-condition-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,MAAM,MAAM,wBAAwB,GAChC,uBAAuB,GACvB,mBAAmB,GACnB,eAAe,GACf,gBAAgB,GAChB,cAAc,CAAC;AAEnB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,wBAAwB,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAEhC;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CACtB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,iBAAiB,EAAE,EACvC,mBAAmB,EAAE,MAAM,EAC3B,oBAAoB,EAAE,MAAM,GAC3B,+BAA+B;IAgElC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO;IAyB5E;;;OAGG;IACH,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAO7D;;;;OAIG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAO1D;;;;OAIG;IACH,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC;QAC/C,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IAoBF;;;;;OAKG;IACH,MAAM,CAAC,sBAAsB,CAC3B,eAAe,EAAE,iBAAiB,EAAE,EACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,GAAG,CAAC,MAAM,CAAC;IAmCd;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,sBAAsB;IA6BrC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;CAsD9B"}
@@ -40,23 +40,23 @@ export class RosterConditionEngine {
40
40
  });
41
41
  }
42
42
  }
43
- // 2. Vérifier le jump s'il existe
44
- const jumpTarget = this.extractJumpTarget(condition);
45
- if (jumpTarget) {
46
- if (!availableCodes.has(jumpTarget)) {
43
+ // 2. Vérifier TOUS les jumps (supporte jump() || jump() || ...)
44
+ const allJumps = this.extractAllJumps(condition);
45
+ for (const jump of allJumps) {
46
+ if (!availableCodes.has(jump.target)) {
47
47
  errors.push({
48
48
  type: 'jump_out_of_scope',
49
- variableCode: jumpTarget,
50
- message: `La variable cible \${${jumpTarget}} n'existe pas dans ce Roster`
49
+ variableCode: jump.target,
50
+ message: `La variable cible \${${jump.target}} n'existe pas dans ce Roster`
51
51
  });
52
52
  }
53
53
  else {
54
- const targetVar = availableVariables.find(v => v.code === jumpTarget);
54
+ const targetVar = availableVariables.find(v => v.code === jump.target);
55
55
  if (targetVar && targetVar.ordre <= currentVariableOrdre) {
56
56
  errors.push({
57
57
  type: 'backward_jump',
58
- variableCode: jumpTarget,
59
- message: 'Le jump ne peut pas cibler une variable précédente'
58
+ variableCode: jump.target,
59
+ message: `Le jump vers \${${jump.target}} ne peut pas cibler une variable précédente`
60
60
  });
61
61
  }
62
62
  }
@@ -113,8 +113,9 @@ export class RosterConditionEngine {
113
113
  return matches.map(m => m.slice(2, -1));
114
114
  }
115
115
  /**
116
- * Extrait la cible d'un jump s'il existe dans la condition
116
+ * Extrait la cible d'un jump s'il existe dans la condition (premier match uniquement)
117
117
  * Format: jump(condition, ${TARGET})
118
+ * @deprecated Utiliser extractAllJumps pour supporter les jumps multiples
118
119
  */
119
120
  static extractJumpTarget(condition) {
120
121
  if (!condition)
@@ -122,8 +123,32 @@ export class RosterConditionEngine {
122
123
  const match = condition.match(/jump\s*\([^,]+,\s*\$\{([A-Z_][A-Z0-9_]*)\}\s*\)/);
123
124
  return match ? match[1] : null;
124
125
  }
126
+ /**
127
+ * Extrait TOUS les jumps d'une condition
128
+ * Supporte: jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
129
+ * Retourne un tableau de { fullExpression, innerCondition, target }
130
+ */
131
+ static extractAllJumps(condition) {
132
+ if (!condition)
133
+ return [];
134
+ const jumps = [];
135
+ // Regex pour capturer chaque jump avec sa condition interne et sa cible
136
+ const jumpRegex = /jump\s*\(\s*([^,]+?)\s*,\s*\$\{([A-Z_][A-Z0-9_]*)\}\s*\)/g;
137
+ let match;
138
+ while ((match = jumpRegex.exec(condition)) !== null) {
139
+ jumps.push({
140
+ fullExpression: match[0],
141
+ innerCondition: match[1].trim(),
142
+ target: match[2]
143
+ });
144
+ }
145
+ return jumps;
146
+ }
125
147
  /**
126
148
  * Calcule les variables qui doivent être masquées à cause des jumps actifs
149
+ * Supporte les jumps multiples avec court-circuit:
150
+ * - jump(cond1, ${VAR1}) || jump(cond2, ${VAR2})
151
+ * - Si cond1 est vraie, on saute à VAR1 et cond2 n'est pas évaluée
127
152
  */
128
153
  static computeJumpedVariables(sortedVariables, lineValues) {
129
154
  const jumped = new Set();
@@ -131,24 +156,60 @@ export class RosterConditionEngine {
131
156
  const condition = rosterVar.conditionsAffichage;
132
157
  if (!condition)
133
158
  continue;
134
- const jumpTarget = this.extractJumpTarget(condition);
135
- if (!jumpTarget)
159
+ // Extraire tous les jumps de cette condition
160
+ const allJumps = this.extractAllJumps(condition);
161
+ if (allJumps.length === 0)
136
162
  continue;
137
- // Évaluer si le jump doit s'activer
138
- if (this.evaluate(condition, lineValues)) {
139
- const targetVar = sortedVariables.find(v => v.code === jumpTarget);
140
- if (targetVar) {
141
- // Masquer toutes les variables entre la source et la cible
142
- for (const v of sortedVariables) {
143
- if (v.ordre > rosterVar.ordre && v.ordre < targetVar.ordre) {
144
- jumped.add(v.code);
163
+ // Évaluer les jumps dans l'ordre avec court-circuit
164
+ for (const jump of allJumps) {
165
+ // Évaluer la condition interne du jump
166
+ const innerConditionResult = this.evaluateInnerCondition(jump.innerCondition, lineValues);
167
+ if (innerConditionResult) {
168
+ // Ce jump s'active - masquer les variables entre source et cible
169
+ const targetVar = sortedVariables.find(v => v.code === jump.target);
170
+ if (targetVar) {
171
+ for (const v of sortedVariables) {
172
+ if (v.ordre > rosterVar.ordre && v.ordre < targetVar.ordre) {
173
+ jumped.add(v.code);
174
+ }
145
175
  }
146
176
  }
177
+ // Court-circuit: on ne vérifie pas les jumps suivants
178
+ break;
147
179
  }
148
180
  }
149
181
  }
150
182
  return jumped;
151
183
  }
184
+ /**
185
+ * Évalue une condition interne (sans le wrapper jump())
186
+ */
187
+ static evaluateInnerCondition(innerCondition, lineValues) {
188
+ if (!innerCondition?.trim())
189
+ return false;
190
+ try {
191
+ // Construire un contexte isolé pour cette ligne du roster
192
+ const responses = {};
193
+ for (const [code, value] of Object.entries(lineValues)) {
194
+ responses[code] = {
195
+ variableCode: code,
196
+ valeur: value,
197
+ dateModification: new Date()
198
+ };
199
+ }
200
+ const engine = new ConditionEngine(responses);
201
+ // Si c'est déjà un showMe(), l'évaluer directement
202
+ if (innerCondition.trim().startsWith('showMe(')) {
203
+ return engine.evaluate(innerCondition);
204
+ }
205
+ // Sinon, wrapper dans showMe pour évaluation
206
+ return engine.evaluate(`showMe(${innerCondition})`);
207
+ }
208
+ catch (error) {
209
+ console.warn('[RosterConditionEngine] Erreur évaluation condition interne:', innerCondition, error);
210
+ return false;
211
+ }
212
+ }
152
213
  /**
153
214
  * Valide la syntaxe de base d'une condition
154
215
  */
@@ -173,6 +234,31 @@ export class RosterConditionEngine {
173
234
  if (!functionMatch && !condition.includes('${')) {
174
235
  return 'Condition invalide: utilisez showMe(), hideMe() ou jump()';
175
236
  }
237
+ // Vérifier que les jumps multiples utilisent uniquement ||
238
+ const allJumps = this.extractAllJumps(condition);
239
+ if (allJumps.length > 1) {
240
+ // Retirer tous les jumps et vérifier ce qui reste entre eux
241
+ let remaining = condition;
242
+ for (const jump of allJumps) {
243
+ remaining = remaining.replace(jump.fullExpression, '###JUMP###');
244
+ }
245
+ // Entre les ###JUMP###, seul || est autorisé (avec espaces optionnels)
246
+ const betweenJumps = remaining.split('###JUMP###').slice(1, -1);
247
+ for (const between of betweenJumps) {
248
+ const trimmed = between.trim();
249
+ if (trimmed !== '||') {
250
+ return `Seul l'opérateur || est autorisé entre les jumps multiples (trouvé: "${trimmed || 'rien'}")`;
251
+ }
252
+ }
253
+ // Vérifier aussi avant le premier et après le dernier jump
254
+ const parts = remaining.split('###JUMP###');
255
+ if (parts[0].trim() !== '') {
256
+ return `Syntaxe invalide avant le premier jump: "${parts[0].trim()}"`;
257
+ }
258
+ if (parts[parts.length - 1].trim() !== '') {
259
+ return `Syntaxe invalide après le dernier jump: "${parts[parts.length - 1].trim()}"`;
260
+ }
261
+ }
176
262
  return null;
177
263
  }
178
264
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",