From 9c046fb3ca8692f24e519a98768a9b6363e8ffcc Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 15 Nov 2024 14:25:33 -0800 Subject: [PATCH 1/3] refactor: qrcode and latex pipes --- package.json | 1 + .../components/latex/latex.component.ts | 22 ++++++- .../template/components/latex/latex.utils.ts | 48 ++++++++++++++ .../components/qr-code/qr-code.component.html | 2 +- .../components/qr-code/qr-code.component.ts | 18 +++++- .../shared/components/template/pipes/index.ts | 4 -- .../components/template/pipes/latex.pipe.ts | 63 ------------------- .../components/template/pipes/qr-code.pipe.ts | 14 ----- yarn.lock | 10 +++ 9 files changed, 95 insertions(+), 87 deletions(-) create mode 100644 src/app/shared/components/template/components/latex/latex.utils.ts delete mode 100644 src/app/shared/components/template/pipes/latex.pipe.ts delete mode 100644 src/app/shared/components/template/pipes/qr-code.pipe.ts diff --git a/package.json b/package.json index 7c9c402947..d58d763e43 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "@types/marked": "^2.0.3", "@types/node": "^18.18.13", "@types/nouislider": "^15.0.0", + "@types/qrcode": "^1.5.5", "@types/swiper": "~4.2.0", "@typescript-eslint/eslint-plugin": "^6.13.1", "@typescript-eslint/parser": "^6.13.1", diff --git a/src/app/shared/components/template/components/latex/latex.component.ts b/src/app/shared/components/template/components/latex/latex.component.ts index ecc80a9bb1..b970974e8a 100644 --- a/src/app/shared/components/template/components/latex/latex.component.ts +++ b/src/app/shared/components/template/components/latex/latex.component.ts @@ -1,9 +1,25 @@ -import { Component, OnInit } from "@angular/core"; +import { Component, computed } from "@angular/core"; import { TemplateBaseComponent } from "src/app/shared/components/template/components/base"; +import { DomSanitizer } from "@angular/platform-browser"; + +import DOMPurify from "dompurify"; +import { latexToHtml } from "./latex.utils"; + @Component({ selector: "template-latex-component", - template: ` `, + template: ` `, }) /** Render text that includes LaTeX equations **/ -export class TmplLatexComponent extends TemplateBaseComponent {} +export class TmplLatexComponent extends TemplateBaseComponent { + public parsedLatex = computed(() => { + const value = this.value(); + const html = latexToHtml(value); + const sanitizedHtml = DOMPurify.sanitize(html); + return this.domSanitizer.bypassSecurityTrustHtml(sanitizedHtml); + }); + + constructor(private domSanitizer: DomSanitizer) { + super(); + } +} diff --git a/src/app/shared/components/template/components/latex/latex.utils.ts b/src/app/shared/components/template/components/latex/latex.utils.ts new file mode 100644 index 0000000000..47af156dd2 --- /dev/null +++ b/src/app/shared/components/template/components/latex/latex.utils.ts @@ -0,0 +1,48 @@ +import katex from "katex"; +import { extractMath, Segment } from "extract-math"; + +export function latexToHtml(latexInput: string) { + let segments: Segment[] = []; + if (latexInput && typeof latexInput === "string" && latexInput.length >= 0) { + const dollarSignDelimitedSegments = extractMath(latexInput, { + delimiters: { + inline: ["$", "$"], + display: ["$$", "$$"], + }, + }); + + segments = + dollarSignDelimitedSegments.length > 1 + ? dollarSignDelimitedSegments + : extractMath(latexInput, { + delimiters: { + inline: ["\\(", "\\)"], + display: ["\\[", "\\]"], + }, + }); + + let htmlString = `

`; + + // detect whether input string contains a LaTeX 'math' segment, and is therefore in LaTeX's 'paragraph mode', else is itself in 'math mode' + if (segments.some((segment) => segment.math)) { + for (const segment of segments) { + if (segment.math) { + htmlString += katex.renderToString(segment.raw, { + displayMode: segment.type === "display", + throwOnError: true, + }); + } else { + htmlString += segment.value; + } + } + } else { + htmlString += katex.renderToString(latexInput, { + throwOnError: true, + }); + } + htmlString += `

`; + + return htmlString; + } + return latexInput; +} diff --git a/src/app/shared/components/template/components/qr-code/qr-code.component.html b/src/app/shared/components/template/components/qr-code/qr-code.component.html index 34b113dd43..60c14bdd04 100644 --- a/src/app/shared/components/template/components/qr-code/qr-code.component.html +++ b/src/app/shared/components/template/components/qr-code/qr-code.component.html @@ -1,3 +1,3 @@
- {{ _row.value }} +
diff --git a/src/app/shared/components/template/components/qr-code/qr-code.component.ts b/src/app/shared/components/template/components/qr-code/qr-code.component.ts index ce3611ebd3..4955055e6f 100644 --- a/src/app/shared/components/template/components/qr-code/qr-code.component.ts +++ b/src/app/shared/components/template/components/qr-code/qr-code.component.ts @@ -1,4 +1,5 @@ -import { Component } from "@angular/core"; +import { Component, computed, effect } from "@angular/core"; +import QRCode from "qrcode"; import { TemplateBaseComponent } from "../base"; @Component({ @@ -6,4 +7,17 @@ import { TemplateBaseComponent } from "../base"; templateUrl: "./qr-code.component.html", styleUrls: ["./qr-code.component.scss"], }) -export class TmplQRCodeComponent extends TemplateBaseComponent {} +export class TmplQRCodeComponent extends TemplateBaseComponent { + /** + * Computed signal used to generate QR code from value input + * + * NOTE - as computed signals do not accept async values this method + * returns a promise which should be handled via async pipe + * */ + public qrCodeData = computed((): Promise => { + const value = this.value(); + if (value && typeof value === "string") { + return QRCode.toDataURL(value); + } + }); +} diff --git a/src/app/shared/components/template/pipes/index.ts b/src/app/shared/components/template/pipes/index.ts index 99777fc261..1137c13364 100644 --- a/src/app/shared/components/template/pipes/index.ts +++ b/src/app/shared/components/template/pipes/index.ts @@ -2,13 +2,11 @@ import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { FilterDisplayComponentPipe } from "./filter-display-component.pipe"; -import { LatexPipe } from "./latex.pipe"; import { MarkdownPipe } from "./markdown.pipe"; import { NumberingPipe } from "./numbering.pipe"; import { PLHAssetPipe } from "./plh-asset.pipe"; import { StyleListPipe } from "./styleList.pipe"; import { TranslatePipe } from "./translate.pipe"; -import { QRCodePipe } from "./qr-code.pipe"; const TEMPLATE_PIPES = [ FilterDisplayComponentPipe, @@ -17,8 +15,6 @@ const TEMPLATE_PIPES = [ NumberingPipe, StyleListPipe, TranslatePipe, - LatexPipe, - QRCodePipe, ]; @NgModule({ diff --git a/src/app/shared/components/template/pipes/latex.pipe.ts b/src/app/shared/components/template/pipes/latex.pipe.ts deleted file mode 100644 index cae06afd6f..0000000000 --- a/src/app/shared/components/template/pipes/latex.pipe.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; -import { DomSanitizer, SafeHtml } from "@angular/platform-browser"; -import katex from "katex"; -import { extractMath, Segment } from "extract-math"; -import DOMPurify from "dompurify"; - -@Pipe({ - name: "latex", -}) -/* Checks for LaTeX equations within a text string and renders text and LaTeX as HTML. -Accepts delimiters around LaTeX equations either of type $...$ and $$...$$, or \(...\) and \[...\]. -The equations will be formatted as 'inline' or 'display' in accordance with LaTeX's syntax */ -export class LatexPipe implements PipeTransform { - segments: Segment[] = []; - - constructor(private domSanitizer: DomSanitizer) {} - - transform(latexInput: string): SafeHtml { - if (latexInput && typeof latexInput === "string" && latexInput.length >= 0) { - const dollarSignDelimitedSegments = extractMath(latexInput, { - delimiters: { - inline: ["$", "$"], - display: ["$$", "$$"], - }, - }); - - this.segments = - dollarSignDelimitedSegments.length > 1 - ? dollarSignDelimitedSegments - : extractMath(latexInput, { - delimiters: { - inline: ["\\(", "\\)"], - display: ["\\[", "\\]"], - }, - }); - - let htmlString = `

`; - - // detect whether input string contains a LaTeX 'math' segment, and is therefore in LaTeX's 'paragraph mode', else is itself in 'math mode' - if (this.segments.some((segment) => segment.math)) { - for (const segment of this.segments) { - if (segment.math) { - htmlString += katex.renderToString(segment.raw, { - displayMode: segment.type === "display", - throwOnError: true, - }); - } else { - htmlString += segment.value; - } - } - } else { - htmlString += katex.renderToString(latexInput, { - throwOnError: true, - }); - } - htmlString += `

`; - - const sanitizedHtml = DOMPurify.sanitize(htmlString); - return this.domSanitizer.bypassSecurityTrustHtml(sanitizedHtml); - } - return latexInput; - } -} diff --git a/src/app/shared/components/template/pipes/qr-code.pipe.ts b/src/app/shared/components/template/pipes/qr-code.pipe.ts deleted file mode 100644 index 39b34761dd..0000000000 --- a/src/app/shared/components/template/pipes/qr-code.pipe.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; -import QRCode from "qrcode"; - -@Pipe({ - name: "qrCode", -}) -export class QRCodePipe implements PipeTransform { - async transform(value: any, args?: any[]): Promise { - if (value && typeof value === "string") { - return await QRCode.toDataURL(value); - } - return value; - } -} diff --git a/yarn.lock b/yarn.lock index ac560b8f18..d19a1b98ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7737,6 +7737,15 @@ __metadata: languageName: node linkType: hard +"@types/qrcode@npm:^1.5.5": + version: 1.5.5 + resolution: "@types/qrcode@npm:1.5.5" + dependencies: + "@types/node": "*" + checksum: d92c1d3e77406bf13a03ec521b2ffb1ac99b2e6ea3a17cad670f2610f62e1293554c57e4074bb2fd4e9369f475f863b69e0ae8c543cb049c4a3c1b0c2d92522a + languageName: node + linkType: hard + "@types/qs@npm:*": version: 6.9.11 resolution: "@types/qs@npm:6.9.11" @@ -14997,6 +15006,7 @@ __metadata: "@types/marked": ^2.0.3 "@types/node": ^18.18.13 "@types/nouislider": ^15.0.0 + "@types/qrcode": ^1.5.5 "@types/swiper": ~4.2.0 "@typescript-eslint/eslint-plugin": ^6.13.1 "@typescript-eslint/parser": ^6.13.1 From 2c19b4ce8317eb1b56a4760eedd3c1ebd3b286fe Mon Sep 17 00:00:00 2001 From: chrismclarke Date: Fri, 15 Nov 2024 14:52:59 -0800 Subject: [PATCH 2/3] chore: code tidying --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d58d763e43..2e87be6b0a 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "test:e2e": "yarn workspace test-e2e start", "lint": "ng lint", "lint:docs": "cspell \"documentation/docs/**/*.md\" --config \"cspell.config.yml\"", - "analyse": "ng build --configuration=production --stats-json && npx webpack-bundle-analyzer www/stats.json", + "analyse": "yarn workflow optimise_build && ng build --configuration=production --stats-json && npx webpack-bundle-analyzer www/stats.json", "version": "yarn workspace scripts start version", "format": "yarn prettier . --write --log-level silent" }, From 7441228d5bd0211a6a4fba63bc90138c8455caa8 Mon Sep 17 00:00:00 2001 From: Johnny McQuade Date: Wed, 27 Nov 2024 12:55:00 +0000 Subject: [PATCH 3/3] fix: get boolean param from template row --- src/app/shared/utils/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/utils/utils.ts b/src/app/shared/utils/utils.ts index 23b7ded270..6c9723d355 100644 --- a/src/app/shared/utils/utils.ts +++ b/src/app/shared/utils/utils.ts @@ -208,7 +208,7 @@ export function getBooleanParamFromTemplateRow( _default: boolean ): boolean { const params = row.parameter_list || {}; - return params.hasOwnProperty(name) ? params[name] === "true" : _default; + return params.hasOwnProperty(name) ? parseBoolean(params[name]) : _default; } export function getAnswerListParamFromTemplateRow(