move react projects to own folder

This commit is contained in:
2026-06-11 14:49:21 +02:00
parent 81ecc4d3f5
commit d8699ee4b5
35 changed files with 2 additions and 2 deletions
@@ -0,0 +1,34 @@
@import '~@fluentui/react/dist/sass/References.scss';
.elixFormsComponent {
overflow: hidden;
padding: 1em;
color: "[theme:bodyText, default: #323130]";
color: var(--bodyText);
&.teams {
font-family: $ms-font-family-fallbacks;
}
}
.welcome {
text-align: center;
}
.welcomeImage {
width: 100%;
max-width: 420px;
}
.links {
a {
text-decoration: none;
color: "[theme:link, default:#03787c]";
color: var(--link); // note: CSS Custom Properties support is limited to modern browsers only
&:hover {
text-decoration: underline;
color: "[theme:linkHovered, default: #014446]";
color: var(--linkHovered); // note: CSS Custom Properties support is limited to modern browsers only
}
}
}
+16
View File
@@ -0,0 +1,16 @@
//import * as React from 'react';
import ElixFormsComponentAbstract from './ElixFormsComponentAbstract';
export default class ElixFormsComponent extends ElixFormsComponentAbstract {
// public override createCustomFormFields(): React.ReactElement {
// // const textTest = this.CreateTextInput("COL0018", "TEXT INPUT");
// // const checkTest = this.CreateCheckboxInput("COL0090", "CHECKBOX INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// // const radioTest = this.CreateRadioInput("COL0091", "RADIO INPUT", new Map<number, string>([ [1, "Option 1"], [2, "Option 2"], [3, "Option 3"] ]));
// // const boolTest = this.CreateBooleanInput("COL0092", "BOOLEAN INPUT");
// // const textareaTest = this.CreateTextAreaInput("COL0093", "TEXTAREA INPUT");
// return (<></>);
// }
}
export { ElixFormsComponent as ElixFormsReact };
@@ -0,0 +1,366 @@
import * as FluentUI from '@fluentui/react';
import type { IElixFormsComponentFormState } from './IElixFormsComponentFormState';
import type { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
import { ElixFormsElement } from './ElixFormsElement';
import { QueryParamHelper } from './QueryParamHelper';
import { Component, type FormEvent, type JSX, useState } from 'react';
import React from 'react';
import type { IElixFormsComponentCustomFormFieldFactory } from './IElixFormsComponentCustomFormFieldFactory';
import type { ElixFormsCheckboxOption, ElixFormsDropdownOption, ElixFormsRadioOption } from './ElixFormsTypes';
export default abstract class ElixFormsComponentAbstract extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
constructor(props: IElixFormsComponentProperties) {
super(props);
this.state = {
formData: {}
};
// const [formData, setFormData] = useState({});
// const handleAnyInputChange = (name: string) => (event: any, valueOrOption: any) => { //ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SyntheticEvent<HTMLElement, Event>
// let value: any = null;
// // Fluent UI Dropdown passes (event, option)
// if (valueOrOption && valueOrOption.key !== undefined) {
// value = valueOrOption.key;
// }
// // Fluent UI TextField passes (event, newValue)
// else if (typeof valueOrOption === "string") {
// value = valueOrOption;
// }
// // Fluent UI Checkbox passes (event, checked)
// else if (typeof valueOrOption === "boolean") {
// value = valueOrOption;
// }
// // Native HTML inputs
// else if (event?.target) {
// value = event.target.type === "checkbox"
// ? event.target.checked
// : event.target.value;
// }
// setFormData(prev => ({
// ...prev,
// [name]: value
// }));
// console.log(`Input ${name} changed to ${value}. Current form data:`, JSON.stringify(formData));
// };
}
private checkboxValues = new Map<string, number[]>();
private mandatoryFormFieldNames: string[] = [
"RWE2_MODULE_ID",
"RWE2_REQUEST_ID",
"custom-workflow-back-url",
"custom-workflow-generic-id",
"custom-workflow-current-tabrel-genid",
"custom-workflow-source-field",
"crc",
"MODULE_TESTMODE_KEY",
"ELANG"
];
private createMandatoryFormFields(queryParams: URLSearchParams) : JSX.Element {
const hiddenInputs: JSX.Element[] = [];
// Create a hidden input for each mandatory Elix parameter
this.mandatoryFormFieldNames.forEach(paramName => {
const value = queryParams.get(paramName) ?? "";
// Create the input element
hiddenInputs.push(this.createHiddenInput(paramName));
});
return <>{hiddenInputs}</>;
}
private createAdditionalFormFields() : React.ReactElement {
const schema = JSON.parse(this.props.additionalFieldsJson || "[]");
return (
<>
{schema.map((field: { key: React.Key | null | undefined; }) => (
<div key={field.key}>
{this.renderField(field)}
</div>
))}
</>
);
}
public createCustomFormFields(callback: (formFieldFactory: IElixFormsComponentCustomFormFieldFactory) => JSX.Element = () => <></>): JSX.Element {
const formFieldFactory = {
createBooleanInput: (name: string, label: string, required: boolean = false) =>
this.createBooleanInput(name, label, required),
createCheckboxInput: (name: string, label: string, options: Array<ElixFormsCheckboxOption>) =>
this.createCheckboxInput(name, label, options),
createDropdownInput: (name: string, label: string, options: Array<ElixFormsDropdownOption>, required: boolean = false) =>
this.createDropdownInput(name, label, options, required),
createHiddenInput: (name: string) =>
this.createHiddenInput(name),
createNumberInput: (name: string, label: string, required: boolean = false) =>
this.createNumberInput(name, label, required),
createRadioInput: (name: string, label: string, options: Array<ElixFormsRadioOption>, required: boolean = false) =>
this.createRadioInput(name, label, options, required),
createTextInput: (name: string, label: string, required: boolean = false) =>
this.createTextInput(name, label, required),
createTextAreaInput: (name: string, label: string, required: boolean = false) =>
this.createTextAreaInput(name, label, required),
};
return callback(formFieldFactory);
}
public override render(): React.ReactElement<IElixFormsComponentProperties> {
const {
description,
isDarkTheme,
environmentMessage,
hasTeamsContext,
userDisplayName,
additionalFieldsJson,
customFormFieldsConfiguration
} = this.props;
const queryParams = new URLSearchParams(window.location.search);
return (
<section>
{/* className={`${styles.elixFormsReact} ${hasTeamsContext ? styles.teams : ''}`} */}
<div>{environmentMessage}</div>
<div>{description}</div>
<form action="https://procedure.unipr.it/rwe2/ComeBackToElixAndSave" method="post">
{this.createMandatoryFormFields(queryParams)}
{this.createAdditionalFormFields()}
{this.createCustomFormFields(customFormFieldsConfiguration)}
<input type="submit" value="Submit"/>
</form>
</section>
);
}
private createCheckboxInput(paramName: string, label: string, values: Array<ElixFormsCheckboxOption>) : JSX.Element {
const checkedValues = QueryParamHelper.getCheckedFromQuery(paramName);
if (!this.checkboxValues.get(paramName)) {
this.checkboxValues.set(paramName, checkedValues);
}
const checkboxes: React.ReactElement[] = [];
const stackTokens = { childrenGap: 10 };
values.forEach((entry, entryIndex) => {
const id = `${paramName}_${entryIndex}`;
checkboxes.push(
<FluentUI.Checkbox title={label} key={id} id={id} label={entry.label} required={entry.required ?? false} defaultChecked={checkedValues.indexOf(entryIndex) !== -1} className='isiportalPartialAdminCheckboxFieldItem'
onChange={(event, checked) => {
const currentCheckboxValues = this.checkboxValues.get(paramName)!;
const checkboxIndex = currentCheckboxValues.indexOf(entryIndex);
if (checked && checkboxIndex === -1) {
currentCheckboxValues.push(entryIndex);
} else if (!checked && checkboxIndex !== -1) {
currentCheckboxValues.splice(checkboxIndex, 1);
}
this.state.formData[paramName] = currentCheckboxValues.join(',').trim() ?? '';
//this.setState({ formData: { ...this.state.formData, [paramName]: currentCheckboxValues.join(',').trim() ?? '' } }, () =>
console.log(`Checkbox ${id} changed to ${checked}. Current values for ${paramName}: ${this.state.formData[paramName]}`);
//);
}}
/>
);
});
return new ElixFormsElement(
<><FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label></>,
<><FluentUI.Stack tokens={stackTokens}>
{checkboxes}
</FluentUI.Stack>
<input type='hidden' id={paramName} name={paramName} value={this.state.formData[paramName] ?? ''} />
</>
).render();
}
private createRadioInput(paramName: string, label: string, values: Array<ElixFormsRadioOption>, required: boolean = false) : JSX.Element {
const radioValue = QueryParamHelper.getOptionFromQuery(paramName);
const radios: FluentUI.IChoiceGroupOption[] = [];
values.forEach((entry, _) => {
const id = `${paramName}_${entry.value}`;
radios.push(
{
key: id,
text: entry.label,
id: id,
name: paramName,
value: entry.value,
className: 'isiportalPartialAdminFormFieldRadio'
}
);
});
return new ElixFormsElement(
<></>,
<FluentUI.ChoiceGroup
name={paramName}
label={label}
options={radios}
required={required}
defaultSelectedKey={`${paramName}_${radioValue}`}
onChange={(event, option) => {
const value = option?.value ?? '';
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`Radio ${option?.id} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private createBooleanInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const boolValue = QueryParamHelper.getOptionFromQuery(paramName);
const radios: FluentUI.IChoiceGroupOption[] = [];
[{ "value": "true", "label": "Sì" }, { "value": "false", "label": "No" }].forEach((val) => {
const id = `${paramName}_${val.value}`;
radios.push(
{
key: id,
text: val.label,
id: id,
name: paramName,
value: val.value,
className: 'isiportalPartialAdminFormFieldRadio'
}
);
});
return new ElixFormsElement(
<></>,
<FluentUI.ChoiceGroup
name={paramName}
label={label}
options={radios}
required={required}
defaultSelectedKey={`${paramName}_${boolValue}`}
onChange={(event, option) => {
const value = option?.value ?? '';
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`Boolean ${option?.id} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private createTextAreaInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textareaValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textareaValue} multiline rows={4} required={required} className='isiportalPartialAdminFormFieldMultiLineText'
onChange={(event, value) => {
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`TextArea ${paramName} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private createTextInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} required={required} className='isiportalPartialAdminFormFieldSingleLineText'
onChange={(event, value) => {
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`TextInput ${paramName} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private createHiddenInput(paramName: string) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return <><input type='text' id={paramName} name={paramName} defaultValue={textValue} /><br/></>;
}
private createNumberInput(paramName: string, label: string, required: boolean = false) : JSX.Element {
const textValue = QueryParamHelper.getDecodedTextFromQuery(paramName) ?? "";
return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.TextField id={paramName} name={paramName} defaultValue={textValue} type="number" required={required} className='isiportalPartialAdminFormFieldSingleLineText'
onChange={(event, value) => {
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`NumberInput ${paramName} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private createDropdownInput(paramName: string, label: string, values: Array<ElixFormsDropdownOption>, required: boolean = false): JSX.Element {
const selectedValue = QueryParamHelper.getOptionFromQuery(paramName);
const options: FluentUI.IDropdownOption[] = [];
values.forEach(entry => {
const id = `${paramName}_${entry.value}`;
options.push(
{
key: id,
text: entry.label
}
);
});
return new ElixFormsElement(
<FluentUI.Label htmlFor={paramName}>{label}</FluentUI.Label>,
<FluentUI.Dropdown
id={paramName}
options={options}
selectedKey={selectedValue?.toString()}
placeholder='---'
required={required}
className='isiportalPartialAdminFormFieldSelect'
onChange={(event, value) => {
this.setState({ formData: { ...this.state.formData, [paramName]: value } }, () =>
console.log(`Dropdown ${paramName} changed to ${value}. Current value for ${paramName}: ${this.state.formData[paramName]}`)
);
}}
/>
).render();
}
private renderField(field: any): JSX.Element {
//const value = this.state.formData[field.key] || "";
switch (field.type) {
case "text":
return this.createTextInput(field.key, field.label, field.required ?? false);
case "textarea":
return this.createTextAreaInput(field.key, field.label, field.required ?? false);
case "radio":
return this.createRadioInput(field.key, field.label,
new Array<ElixFormsRadioOption>().concat(...field.options.map((o: any) => [[o.value, o.label]])),
field.required ?? false);
case "boolean":
return this.createBooleanInput(field.key, field.label, field.required ?? false);
case "checkbox":
return this.createCheckboxInput(field.key, field.label,
new Array<ElixFormsCheckboxOption>().concat(...field.options.map((o: any) => [[o.value, o.label, o.required ?? false]])));
case "number":
return this.createNumberInput(field.key, field.label, field.required ?? false);
case "dropdown":
return this.createDropdownInput(field.key, field.label,
new Array<ElixFormsDropdownOption>().concat(...field.options.map((o: any) => [[o.value, o.label]])),
field.required ?? false);
default:
return <></>;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
import React, { type JSX } from "react";
// Class to encapsulate the label and input elements
export class ElixFormsElement {
label: JSX.Element;
input: JSX.Element;
separator: string;
constructor(labelElement: React.ReactElement, inputElement: React.ReactElement, separator: string = "")
{
if (!labelElement || !inputElement) {
throw new Error("labelElement and inputElement are required.");
}
this.label = labelElement;
this.input = inputElement;
this.separator = separator;
}
// Optional: render both elements together
render() : JSX.Element {
return (<><div className="iuFieldContainer">
<div className="attrDisplay_left attrDisplay_label"><div className="iuLabelContainer"><div className="attrDisplay_center"><div className="attrDisplay_middle">
{this.label}{this.separator}
</div></div><div className="iuClearContainer">&nbsp;</div></div></div>
<div className="attrDisplay_right attrDisplay_input"><div className="iuInputContainer iuTypeString"><div className="attrDisplay_center"><div className="attrDisplay_middle">
{this.input}
</div></div><div className="iuClearContainer">&nbsp;</div></div></div>
</div></>);
}
}
+3
View File
@@ -0,0 +1,3 @@
export type ElixFormsCheckboxOption = { value: number, label: string, required?: boolean };
export type ElixFormsDropdownOption = { value: number, label: string };
export type ElixFormsRadioOption = { value: number, label: string };
@@ -0,0 +1,13 @@
import type { JSX } from "react";
import type { ElixFormsCheckboxOption, ElixFormsDropdownOption, ElixFormsRadioOption } from "./ElixFormsTypes";
export type IElixFormsComponentCustomFormFieldFactory = {
createBooleanInput: (name: string, label: string, required?: boolean) => JSX.Element;
createCheckboxInput: (name: string, label: string, options: Array<ElixFormsCheckboxOption>) => JSX.Element;
createDropdownInput: (name: string, label: string, options: Array<ElixFormsDropdownOption>, required?: boolean) => JSX.Element;
createHiddenInput: (name: string) => JSX.Element;
createNumberInput: (name: string, label: string, required?: boolean) => JSX.Element;
createRadioInput: (name: string, label: string, options: Array<ElixFormsRadioOption>, required?: boolean) => JSX.Element;
createTextInput: (name: string, label: string, required?: boolean) => JSX.Element;
createTextAreaInput: (name: string, label: string, required?: boolean) => JSX.Element;
};
@@ -0,0 +1,3 @@
export interface IElixFormsComponentFormState {
formData: { [key: string]: any; };
}
@@ -0,0 +1,72 @@
import type { JSX } from "react";
import type { IElixFormsComponentCustomFormFieldFactory } from "./IElixFormsComponentCustomFormFieldFactory";
export interface IElixFormsComponentProperties {
description: string;
isDarkTheme: boolean;
environmentMessage: string;
hasTeamsContext: boolean;
userDisplayName: string;
additionalFieldsJson?: string;
customFormFieldsConfiguration?: (formFieldFactory: IElixFormsComponentCustomFormFieldFactory) => JSX.Element;
}
// Additional Fields JSON Schema
/* // To test, just copy-paste the following JSON in the "Additional Fields JSON" property of the web part. Remember to remove comments before pasting.
[
{
"key": "field1",
"type": "text",
"label": "Field 1",
"required": true
},
{
"key": "field2",
"type": "checkbox",
"label": "Field 2",
"options": [
{ "value": 1, "label": "Checkbox Option 1" },
{ "value": 2, "label": "Checkbox Option 2" },
{ "value": 3, "label": "Checkbox Option 3" }
]
},
{
"key": "field3",
"type": "radio",
"label": "Field 3",
"required": true,
"options": [
{ "value": 1, "label": "Radio Option 1" },
{ "value": 2, "label": "Radio Option 2" },
{ "value": 3, "label": "Radio Option 3" }
]
},
{
"key": "field4",
"type": "boolean",
"label": "Field 4"
},
{
"key": "field5",
"type": "textarea",
"label": "Field 5",
"required": true
},
{
"key": "field6",
"type": "number",
"label": "Field 6"
},
{
"key": "field7",
"type": "dropdown",
"label": "Field 7",
"required": true,
"options": [
{ "value": 1, "label": "Dropdown Option 1" },
{ "value": 2, "label": "Dropdown Option 2" },
{ "value": 3, "label": "Dropdown Option 3" }
]
}
]
*/
+41
View File
@@ -0,0 +1,41 @@
export class QueryParamHelper {
public static getCheckedFromQuery(paramName: string): number[] {
const params = new URLSearchParams(window.location.search);
const values = params.get(paramName) ?? "";
console.debug(`getCheckedFromQuery(${paramName}) = ${values}`);
return values.split(',')
.map(Number)
.filter(n => !isNaN(n));
}
public static getOptionFromQuery(paramName: string): number | undefined {
const params = new URLSearchParams(window.location.search);
const num = Number.parseInt(params.get(paramName) ?? "-1");
console.debug(`getOptionFromQuery(${paramName}) = ${num}`);
return num;
}
public static getBooleanFromQuery(paramName: string): boolean | undefined {
const params = new URLSearchParams(window.location.search);
const boolValue = params.get(paramName) === undefined || params.get(paramName) === null ? undefined :
params.get(paramName)?.toLowerCase() === "true" ? true : false;
console.debug(`getBooleanFromQuery(${paramName}) = ${boolValue}`);
return boolValue;
}
public static getDecodedTextFromQuery(paramName: string): string | undefined {
const params = new URLSearchParams(window.location.search);
const textValue = params.get(paramName) === undefined || params.get(paramName) === null ? undefined :
decodeURIComponent(params.get(paramName) as string);
console.debug(`getDecodedTextFromQuery(${paramName}) = ${textValue}`);
return textValue;
}
}