diff --git a/projects/ngx-coding-components/src/lib/translations/de.json b/projects/ngx-coding-components/src/lib/translations/de.json index 6eb5ab7..d60db85 100644 --- a/projects/ngx-coding-components/src/lib/translations/de.json +++ b/projects/ngx-coding-components/src/lib/translations/de.json @@ -263,6 +263,18 @@ "error-unselected-source": "Der Ausdruck verweist auf nicht ausgewählte Quelle(n)", "error-invalid-value": "Bitte numerischen Testwert prüfen", "error-evaluation": "Der Ausdruck konnte nicht ausgewertet werden. Bitte Syntax, Variablennamen und numerische Testwerte prüfen." + }, + "solver-help": { + "title": "Syntaxhilfe für Solver-Ausdrücke", + "description": "Solver-Ausdrücke werden mit math.js ausgewertet. Verwenden Sie Zahlen, Rechenzeichen und math.js-Funktionen; Quellvariablen referenzieren Sie mit ${Variablenname} oder ${VariablenID}.", + "examples-title": "Beispiele:", + "examples": { + "arithmetic": "einfache Rechenoperationen", + "variables": "Werte aus zwei Quellvariablen addieren", + "function": "Prozentwert berechnen und runden", + "condition": "Bedingung: Ergebnis ist 1, wenn Punkte mindestens 5 sind, sonst 0" + }, + "docs-link": "math.js-Syntax öffnen" } }, "processing": { diff --git a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.html b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.html index 98ac826..1d5a740 100644 --- a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.html +++ b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.html @@ -44,12 +44,39 @@

{{ (newVariableMode ? 'varList.add' : 'derive-processing.pr } } @if (data.sourceType === 'SOLVER') { - + {{ 'derive-processing.SOLVER_EXPRESSION' | translate }} +
+
+ + + {{ 'derive-processing.solver-help.title' | translate }} + +
+

{{ 'derive-processing.solver-help.description' | translate }}

+
{{ 'derive-processing.solver-help.examples-title' | translate }}
+
    + @for (example of solverExpressionExamples; track example.expression) { +
  • + {{ example.expression }} + - + {{ example.descriptionKey | translate }} +
  • + } +
+ + {{ 'derive-processing.solver-help.docs-link' | translate }} + + +
+
{{ 'derive-processing.solver-test.title' | translate }}
diff --git a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.spec.ts b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.spec.ts index 4d8c8ad..86f2811 100644 --- a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.spec.ts +++ b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.spec.ts @@ -238,4 +238,21 @@ describe('EditSourceParametersDialog', () => { expect(dialog.solverTestValues).toEqual({ v2: '' }); expect(dialog.solverTestResult).toBeNull(); }); + + it('should provide solver expression help examples and docs link', () => { + const dialog = createDialog({ sourceType: 'SOLVER' }); + const expressions = dialog.solverExpressionExamples.map( + example => example.expression + ); + + expect(dialog.solverExpressionDocsUrl).toBe( + 'https://mathjs.org/docs/expressions/syntax.html' + ); + expect(expressions).toContain('1 + 2 * 3'); + expect(expressions.some(expression => expression.includes('${'))).toBeTrue(); + expect(expressions.some(expression => expression.includes('round('))).toBeTrue(); + expect(expressions.some( + expression => expression.includes('?') && expression.includes(':') + )).toBeTrue(); + }); }); diff --git a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.ts b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.ts index d1ddd30..6608a31 100644 --- a/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.ts +++ b/projects/ngx-coding-components/src/lib/var-coding/dialogs/edit-source-parameters-dialog.component.ts @@ -47,8 +47,123 @@ type SolverTestResult = { message: string; }; +interface SolverExpressionExample { + expression: string; + descriptionKey: string; +} + +const SOLVER_VARIABLE_PREFIX = '$'; + @Component({ templateUrl: 'edit-source-parameters-dialog.component.html', + styles: [` + .solver-expression-field { + width: 100%; + } + + .solver-expression-help { + background: #f5f7ff; + border-left: 4px solid #3f51b5; + border-radius: 4px; + color: rgb(0 0 0 / 82%); + margin: -8px 0 8px; + max-width: 640px; + padding: 12px 14px; + } + + .solver-expression-help__header { + align-items: center; + display: flex; + gap: 8px; + font-weight: 500; + } + + .solver-expression-help__icon { + color: #3f51b5; + font-size: 20px; + height: 20px; + width: 20px; + } + + .solver-expression-help p { + margin: 8px 0; + } + + .solver-expression-help ul { + margin: 6px 0 10px; + padding-left: 20px; + } + + .solver-expression-help li { + margin-bottom: 4px; + } + + .solver-expression-help code { + background: rgb(0 0 0 / 6%); + border-radius: 3px; + overflow-wrap: anywhere; + padding: 1px 4px; + white-space: normal; + } + + .solver-expression-help__link { + align-items: center; + display: inline-flex; + gap: 4px; + } + + .solver-expression-help__link mat-icon { + font-size: 16px; + height: 16px; + width: 16px; + } + `, + ` + .solver-test-area { + border-top: 1px solid rgba(0, 0, 0, 0.12); + margin-top: 8px; + padding-top: 16px; + } + + .solver-test-header { + align-items: center; + display: flex; + gap: 16px; + justify-content: space-between; + margin-bottom: 12px; + } + + .solver-test-title { + font-weight: 600; + } + + .solver-test-values { + display: grid; + gap: 8px 12px; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + } + + .solver-test-result { + border-radius: 4px; + margin-top: 8px; + padding: 8px 12px; + } + + .solver-test-result-ok { + background: #e8f5e9; + color: #1b5e20; + } + + .solver-test-result-error { + background: #ffebee; + color: #b71c1c; + } + + .solver-test-hint { + color: rgba(0, 0, 0, 0.6); + margin-bottom: 8px; + } + `], standalone: true, imports: [ MatSelectTrigger, @@ -58,7 +173,6 @@ type SolverTestResult = { MatButton, MatDialogClose, TranslateModule, - MatIcon, FormsModule, MatFormField, MatInput, @@ -69,58 +183,34 @@ type SolverTestResult = { KeyValuePipe, NgForOf, ReactiveFormsModule, - NgIf - ], - styles: [ - ` - .solver-test-area { - border-top: 1px solid rgba(0, 0, 0, 0.12); - margin-top: 8px; - padding-top: 16px; - } - - .solver-test-header { - align-items: center; - display: flex; - gap: 16px; - justify-content: space-between; - margin-bottom: 12px; - } - - .solver-test-title { - font-weight: 600; - } - - .solver-test-values { - display: grid; - gap: 8px 12px; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - } - - .solver-test-result { - border-radius: 4px; - margin-top: 8px; - padding: 8px 12px; - } - - .solver-test-result-ok { - background: #e8f5e9; - color: #1b5e20; - } - - .solver-test-result-error { - background: #ffebee; - color: #b71c1c; - } - - .solver-test-hint { - color: rgba(0, 0, 0, 0.6); - margin-bottom: 8px; - } - ` + NgIf, + MatIcon ] }) export class EditSourceParametersDialog { + readonly solverExpressionDocsUrl = + 'https://mathjs.org/docs/expressions/syntax.html'; + + readonly solverExpressionExamples: SolverExpressionExample[] = [ + { + expression: '1 + 2 * 3', + descriptionKey: 'derive-processing.solver-help.examples.arithmetic' + }, + { + expression: `${SOLVER_VARIABLE_PREFIX}{Punkte} + ${SOLVER_VARIABLE_PREFIX}{Bonus}`, + descriptionKey: 'derive-processing.solver-help.examples.variables' + }, + { + expression: `round(${SOLVER_VARIABLE_PREFIX}{Punkte} / ` + + `${SOLVER_VARIABLE_PREFIX}{MaxPunkte} * 100, 1)`, + descriptionKey: 'derive-processing.solver-help.examples.function' + }, + { + expression: `${SOLVER_VARIABLE_PREFIX}{Punkte} >= 5 ? 1 : 0`, + descriptionKey: 'derive-processing.solver-help.examples.condition' + } + ]; + sourceTypeList: SourceType[] = [ 'COPY_VALUE', 'CONCAT_CODE', diff --git a/src/assets/de.json b/src/assets/de.json index acc527b..e9e3298 100644 --- a/src/assets/de.json +++ b/src/assets/de.json @@ -94,7 +94,38 @@ "derive-method": { "label": "Ableitungsmethode", "prompt": "Ableitungsmethode", - "BASE": "Basisvariable" + "BASE": "Basisvariable", + "COPY_VALUE": "Wert kopieren", + "CONCAT_CODE": "Codes aneinanderhängen", + "SUM_CODE": "Codes summieren", + "SUM_SCORE": "Bewertung summieren", + "UNIQUE_VALUES": "Auf Einzigartigkeit prüfen", + "SOLVER": "Mathematischer Ausdruck (Solver)", + "MANUAL": "Manuell" + }, + "derive-processing": { + "prompt": "Basis-/Ableitungsparameter", + "TO_LOWER_CASE": "In Kleinbuchstaben umwandeln", + "TO_NUMBER": "In Zahl umwandeln", + "REMOVE_ALL_SPACES": "Alle Leerzeichen entfernen", + "REMOVE_DISPENSABLE_SPACES": "Überflüssige Leerzeichen entfernen", + "TAKE_DISPLAYED_AS_VALUE_CHANGED": "Angezeigt = Wert geändert", + "TAKE_NOT_REACHED_AS_VALUE_CHANGED": "Nicht erreicht = Wert geändert", + "TAKE_EMPTY_AS_VALID": "Leerer Antwortwert ist gültig", + "SORT": "Sortiere Werte", + "SOLVER_EXPRESSION": "Ausdruck für Solver", + "solver-help": { + "title": "Syntaxhilfe für Solver-Ausdrücke", + "description": "Solver-Ausdrücke werden mit math.js ausgewertet. Verwenden Sie Zahlen, Rechenzeichen und math.js-Funktionen; Quellvariablen referenzieren Sie mit ${Variablenname} oder ${VariablenID}.", + "examples-title": "Beispiele:", + "examples": { + "arithmetic": "einfache Rechenoperationen", + "variables": "Werte aus zwei Quellvariablen addieren", + "function": "Prozentwert berechnen und runden", + "condition": "Bedingung: Ergebnis ist 1, wenn Punkte mindestens 5 sind, sonst 0" + }, + "docs-link": "math.js-Syntax öffnen" + } }, "processing": { "prompt": "Verarbeitung", diff --git a/src/assets/en.json b/src/assets/en.json index 4b4aa00..181b803 100644 --- a/src/assets/en.json +++ b/src/assets/en.json @@ -7,11 +7,37 @@ "CONCAT_CODE": "Concatenate codes", "SUM_CODE": "Sum codes", "SUM_SCORE": "Sum scores", + "UNIQUE_VALUES": "Check uniqueness", + "SOLVER": "Mathematical expression (solver)", "MANUAL": "Manual" }, "derive-sources": { "label": "Source variables", "prompt": "Source variable(s)", "error": "No source defined" + }, + "derive-processing": { + "prompt": "Base/derivation parameters", + "TO_LOWER_CASE": "Convert to lower case", + "TO_NUMBER": "Convert to number", + "REMOVE_ALL_SPACES": "Remove all spaces", + "REMOVE_DISPENSABLE_SPACES": "Remove dispensable spaces", + "TAKE_DISPLAYED_AS_VALUE_CHANGED": "Displayed = value changed", + "TAKE_NOT_REACHED_AS_VALUE_CHANGED": "Not reached = value changed", + "TAKE_EMPTY_AS_VALID": "Empty response value is valid", + "SORT": "Sort values", + "SOLVER_EXPRESSION": "Expression for solver", + "solver-help": { + "title": "Syntax help for solver expressions", + "description": "Solver expressions are evaluated with math.js. Use numbers, operators and math.js functions; refer to source variables with ${VariableName} or ${VariableID}.", + "examples-title": "Examples:", + "examples": { + "arithmetic": "simple arithmetic", + "variables": "add values from two source variables", + "function": "calculate and round a percentage", + "condition": "condition: result is 1 when points are at least 5, otherwise 0" + }, + "docs-link": "Open math.js syntax" + } } }