mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2024-06-03 06:10:11 +02:00
GenericFields
This commit is contained in:
parent
20f027bb5a
commit
13bfa0b0d0
|
@ -5,6 +5,8 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/stirling-pdf-logo.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tauri + React + TS</title>
|
||||
|
||||
<script src="browserfs.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -1,15 +1,31 @@
|
|||
import Joi from "@stirling-tools/joi";
|
||||
|
||||
import { GenericField } from "./GenericField";
|
||||
import React from "react";
|
||||
|
||||
interface BuildFieldsProps {
|
||||
/** The text to display inside the button */
|
||||
schemaDescription: Joi.Description | undefined;
|
||||
onSubmit: React.FormEventHandler<HTMLFormElement>;
|
||||
}
|
||||
|
||||
|
||||
export function BuildFields({ schemaDescription }: BuildFieldsProps) {
|
||||
export function BuildFields({ schemaDescription, onSubmit }: BuildFieldsProps) {
|
||||
console.log("Render Build Fields", schemaDescription);
|
||||
const label = (schemaDescription?.flags as any)?.label
|
||||
const description = (schemaDescription?.flags as any)?.description;
|
||||
const values = (schemaDescription?.keys as any)?.values.keys as { [key: string]: Joi.Description};
|
||||
return (
|
||||
<div>Description: {(schemaDescription?.flags as any)?.description}</div>
|
||||
<div>
|
||||
<h3>{label}</h3>
|
||||
{description}
|
||||
<hr />
|
||||
<form onSubmit={(e) => { onSubmit(e); e.preventDefault(); }}>
|
||||
{
|
||||
values ? Object.keys(values).map((key, i) => {
|
||||
return (<GenericField key={key} fieldName={key} joiDefinition={values[key]} />)
|
||||
}) : undefined
|
||||
}
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
57
client-tauri/src/components/fields/GenericField.tsx
Normal file
57
client-tauri/src/components/fields/GenericField.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import Joi from "@stirling-tools/joi";
|
||||
import { Fragment } from "react";
|
||||
|
||||
interface GenericFieldProps {
|
||||
fieldName: string
|
||||
joiDefinition: Joi.Description;
|
||||
}
|
||||
|
||||
export function GenericField({ fieldName, joiDefinition }: GenericFieldProps) {
|
||||
switch (joiDefinition.type) {
|
||||
case "number":
|
||||
var validValues = joiDefinition.allow;
|
||||
if(validValues) { // Restrained text input
|
||||
return (
|
||||
<Fragment>
|
||||
<label htmlFor={fieldName}>{fieldName}:</label>
|
||||
<input type="number" list={fieldName} name={fieldName}/>
|
||||
<datalist id={fieldName}>
|
||||
{joiDefinition.allow.map((e: string) => {
|
||||
return (<option key={e} value={e}/>)
|
||||
})}
|
||||
</datalist>
|
||||
<br/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
else {
|
||||
// TODO: Implement unrestrained text input
|
||||
return (<pre>{JSON.stringify(joiDefinition, null, 2)}</pre>)
|
||||
}
|
||||
break;
|
||||
case "string":
|
||||
var validValues = joiDefinition.allow;
|
||||
if(validValues) { // Restrained text input
|
||||
return (
|
||||
<Fragment>
|
||||
<label htmlFor={fieldName}>{fieldName}:</label>
|
||||
<input type="text" list={fieldName} name={fieldName}/>
|
||||
<datalist id={fieldName}>
|
||||
{joiDefinition.allow.map((e: string) => {
|
||||
return (<option key={e} value={e}/>)
|
||||
})}
|
||||
</datalist>
|
||||
<br/>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
else {
|
||||
// TODO: Implement unrestrained text input
|
||||
return (<pre>{JSON.stringify(joiDefinition, null, 2)}</pre>)
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return (<div>Field "{fieldName}": <br /> requested type "{joiDefinition.type}" not found</div>)
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
import { Link } from "react-router-dom";
|
||||
|
||||
import { BaseSyntheticEvent, createContext, useState } from "react";
|
||||
import { BaseSyntheticEvent, createContext, useRef, useState } from "react";
|
||||
import { Operator } from "@stirling-pdf/shared-operations/src/functions";
|
||||
import i18next from "i18next";
|
||||
import Joi from "@stirling-tools/joi";
|
||||
import { BuildFields } from "../components/fields/BuildFields";
|
||||
import { listOperatorNames } from "@stirling-pdf/shared-operations/src/workflow/operatorAccessor";
|
||||
import { PdfFile, RepresentationType } from "@stirling-pdf/shared-operations/src/wrappers/PdfFile";
|
||||
import { Action } from "@stirling-pdf/shared-operations/declarations/Action";
|
||||
import { JoiPDFFileSchema } from "@stirling-pdf/shared-operations/src/wrappers/PdfFileJoi";
|
||||
|
||||
function Dynamic() {
|
||||
const [schemaDescription, setSchemaDescription] = useState<Joi.Description>();
|
||||
|
||||
|
||||
const operators = ["impose"]; // TODO: Make this dynamic
|
||||
const operators = listOperatorNames();
|
||||
const activeOperator = useRef<typeof Operator>();
|
||||
|
||||
function selectionChanged(s: BaseSyntheticEvent) {
|
||||
const selectedValue = s.target.value;
|
||||
|
@ -19,7 +23,7 @@ function Dynamic() {
|
|||
return;
|
||||
}
|
||||
|
||||
i18next.loadNamespaces("impose", (err, t) => {
|
||||
i18next.loadNamespaces(selectedValue, (err, t) => {
|
||||
if (err) throw err;
|
||||
|
||||
const LoadingModule = import(`@stirling-pdf/shared-operations/src/functions/${selectedValue}`) as Promise<{ [key: string]: typeof Operator }>;
|
||||
|
@ -27,12 +31,77 @@ function Dynamic() {
|
|||
const Operator = Module[capitalizeFirstLetter(selectedValue)];
|
||||
const description = Operator.schema.describe();
|
||||
|
||||
setSchemaDescription(description); // This will update children
|
||||
console.log(description);
|
||||
activeOperator.current = Operator;
|
||||
// This will update children
|
||||
setSchemaDescription(description);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function formDataToObject(formData: FormData): Record<string, string> {
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
formData.forEach((value, key) => {
|
||||
result[key] = value.toString();
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function handleSubmit(e: BaseSyntheticEvent) {
|
||||
if(!activeOperator.current) {
|
||||
throw new Error("Please select an Operator in the Dropdown");
|
||||
}
|
||||
|
||||
const formData = new FormData(e.target);
|
||||
|
||||
const action: Action = {type: activeOperator.current.constructor.name, values: formDataToObject(formData)};
|
||||
|
||||
// Validate PDF File
|
||||
|
||||
// Createing the pdffile before validation because joi cant handle it for some reason and I can't fix the underlying issue / I want to make progress, wasted like 3 hours on this already. TODO: The casting should be done in JoiPDFFileSchema.ts if done correctly...
|
||||
const files = (document.getElementById("pdfFile") as HTMLInputElement).files;
|
||||
const inputs: PdfFile[] = [];
|
||||
|
||||
if(files) {
|
||||
const filesArray: File[] = Array.from(files as any);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = filesArray[i];
|
||||
if(file) {
|
||||
console.log(new Uint8Array(await file.arrayBuffer()));
|
||||
inputs.push(new PdfFile(
|
||||
file.name.replace(/\.[^/.]+$/, ""), // Strip Extension
|
||||
new Uint8Array(await file.arrayBuffer()),
|
||||
RepresentationType.Uint8Array
|
||||
));
|
||||
}
|
||||
else
|
||||
throw new Error("This should not happen. Contact maintainers.");
|
||||
}
|
||||
}
|
||||
|
||||
const pdfValidationResults = await JoiPDFFileSchema.validate(inputs);
|
||||
if(pdfValidationResults.error) {
|
||||
console.log({error: "PDF validation failed", details: pdfValidationResults.error.message});
|
||||
}
|
||||
const pdfFiles: PdfFile[] = pdfValidationResults.value;
|
||||
|
||||
// Validate Action Values
|
||||
const actionValidationResults = activeOperator.current.schema.validate({input: pdfFiles, values: action.values});
|
||||
|
||||
if(actionValidationResults.error) {
|
||||
console.log({error: "Value validation failed", details: actionValidationResults.error.message});
|
||||
return;
|
||||
}
|
||||
|
||||
action.values = pdfValidationResults.value.values;
|
||||
const operation = new activeOperator.current(action);
|
||||
|
||||
operation.run(pdfValidationResults.value, (progress) => {}).then(pdfFiles => {
|
||||
console.log("Done");
|
||||
});
|
||||
};
|
||||
|
||||
function capitalizeFirstLetter(string: String) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
@ -46,17 +115,14 @@ function Dynamic() {
|
|||
<select id="pdfOptions" onChange={selectionChanged}>
|
||||
<option value="none">none</option>
|
||||
{ operators.map((operator, i) => {
|
||||
return (<option value={operator}>{operator}</option>)
|
||||
return (<option key={operator} value={operator}>{operator}</option>)
|
||||
}) }
|
||||
</select>
|
||||
|
||||
<div id="values">
|
||||
<BuildFields schemaDescription={schemaDescription}></BuildFields>
|
||||
<BuildFields schemaDescription={schemaDescription} onSubmit={handleSubmit}></BuildFields>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<button id="processButton">Process process file with current settings</button>
|
||||
|
||||
<p>
|
||||
<Link to="/">Go back home...</Link>
|
||||
</p>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
|
|
|
@ -3,6 +3,7 @@ import react from "@vitejs/plugin-react";
|
|||
import topLevelAwait from "vite-plugin-top-level-await";
|
||||
import dynamicImport from 'vite-plugin-dynamic-import'
|
||||
import compileTime from "vite-plugin-compile-time"
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
|
@ -18,7 +19,11 @@ export default defineConfig(async () => ({
|
|||
compileTime(),
|
||||
dynamicImport(),
|
||||
],
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'#pdfcpu': fileURLToPath(new URL("../shared-operations/src/wasm/pdfcpu/pdfcpu-wrapper.client", import.meta.url))
|
||||
}
|
||||
},
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
|
|
|
@ -3,7 +3,7 @@ const app = express();
|
|||
const PORT = 80;
|
||||
|
||||
// Server Frontend TODO: Make this typescript compatible
|
||||
app.use(express.static('../client-vanilla/public'));
|
||||
app.use(express.static('./public'));
|
||||
app.use(express.static('../shared-operations'));
|
||||
|
||||
// serve
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<script src="/dep/pdf.min.js"></script>
|
||||
<script src="/dep/jsQR.js"></script>
|
||||
|
||||
<script src="/wasm/browserfs.min.js"></script>
|
||||
<script src="/browserfs.min.js"></script>
|
||||
<script src="/wasm/opencv/opencv_3_4_custom_O3.js"></script>
|
||||
|
||||
<script src="index.js" type="module"></script>
|
||||
|
|
|
@ -24,7 +24,7 @@ async function handleEndpoint(req: Request, res: Response) {
|
|||
return;
|
||||
}
|
||||
|
||||
const validationResults = JoiPDFFileSchema.validate(req.files);
|
||||
const validationResults = await JoiPDFFileSchema.validateAsync(req.files);
|
||||
if(validationResults.error) {
|
||||
res.status(400).json({error: "PDF validation failed", details: validationResults.error.message});
|
||||
return;
|
||||
|
|
|
@ -43,7 +43,7 @@ router.post("/:workflowUuid?", [
|
|||
}
|
||||
}
|
||||
|
||||
const validationResults = JoiPDFFileSchema.validate(req.files);
|
||||
const validationResults = await JoiPDFFileSchema.validateAsync(req.files);
|
||||
if(validationResults.error) {
|
||||
res.status(400).json({error: "PDF validation failed", details: validationResults.error.message});
|
||||
return;
|
||||
|
|
2
shared-operations/declarations/Action.d.ts
vendored
2
shared-operations/declarations/Action.d.ts
vendored
|
@ -1,6 +1,6 @@
|
|||
export interface Action {
|
||||
values: any;
|
||||
type: string;
|
||||
type: "wait" | "done" | "impose" | string;
|
||||
actions?: Action[];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { PdfFile } from "wrappers/PdfFile";
|
||||
import { Action } from "../../declarations/Action";
|
||||
import Joi from "@stirling-tools/joi";
|
||||
|
||||
|
@ -33,7 +34,7 @@ export class Operator {
|
|||
this.actionValues = action.values;
|
||||
}
|
||||
|
||||
async run(input: any[], progressCallback: (progress: Progress) => void): Promise<any[]> {
|
||||
async run(input: PdfFile[] | any[], progressCallback: (progress: Progress) => void): Promise<PdfFile[] | any[]> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// imports browserfs via index.html script-tag
|
||||
import wasmUrl from '../../../public/wasm/pdfcpu/pdfcpu.wasm?url'
|
||||
|
||||
let wasmLocation = "/wasm/pdfcpu/";
|
||||
|
||||
|
@ -29,15 +30,12 @@ function configureFs() {
|
|||
}
|
||||
|
||||
function loadWasm() {
|
||||
const script = document.createElement("script");
|
||||
script.src = wasmLocation + "/wasm_exec.js";
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
import("./wasm_exec.js");
|
||||
}
|
||||
|
||||
const runWasm = async (param) => {
|
||||
if (window.cachedWasmResponse === undefined) {
|
||||
const response = await fetch(wasmLocation + "/pdfcpu.wasm");
|
||||
const response = await fetch(wasmUrl);
|
||||
const buffer = await response.arrayBuffer();
|
||||
window.cachedWasmResponse = buffer;
|
||||
window.go = new Go();
|
||||
|
|
Loading…
Reference in New Issue
Block a user