start of ai-based development
This commit is contained in:
@@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-common-library
|
||||||
|
description: >
|
||||||
|
Documentazione della libreria condivisa common/ del progetto elixForms.
|
||||||
|
Descrive i componenti ElixForms, il pattern di form field factory,
|
||||||
|
le interfacce TypeScript, i tipi, e l'helper per i query parameter.
|
||||||
|
Attiva questa skill quando lavori sui componenti condivisi, crei nuovi tipi di campo,
|
||||||
|
modifichi la gestione dello stato del form, o integri con la piattaforma elixForms.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Libreria Common elixForms
|
||||||
|
|
||||||
|
## Panoramica
|
||||||
|
|
||||||
|
La cartella `react/common/` contiene la libreria condivisa di componenti React/TypeScript usata da tutte le pagine custom. È basata su **Fluent UI React v8** e implementa un sistema di form con campi dinamici.
|
||||||
|
|
||||||
|
## Architettura dei Componenti
|
||||||
|
|
||||||
|
```
|
||||||
|
ElixFormsComponentAbstract (abstract class, extends React.Component)
|
||||||
|
└── ElixFormsComponent (classe concreta, exported as ElixFormsReact)
|
||||||
|
└── Usata nelle pagine come `new ElixForms.ElixFormsReact(props)`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gerarchia delle classi
|
||||||
|
|
||||||
|
1. **`ElixFormsComponentAbstract`** (`ElixFormsComponentAbstract.tsx`)
|
||||||
|
- Classe astratta che estende `React.Component<IElixFormsComponentProperties, IElixFormsComponentFormState>`
|
||||||
|
- Contiene tutta la logica di creazione dei campi form (factory methods)
|
||||||
|
- Gestisce lo state del form (`formData: { [key: string]: any }`)
|
||||||
|
- Implementa `render()` che genera `<form>` con action POST verso elixForms
|
||||||
|
|
||||||
|
2. **`ElixFormsComponent`** (`ElixFormsComponent.tsx`)
|
||||||
|
- Classe concreta che estende `ElixFormsComponentAbstract`
|
||||||
|
- Esportata come `ElixFormsReact` per uso nelle pagine
|
||||||
|
- Attualmente vuota (la logica è nell'abstract)
|
||||||
|
|
||||||
|
## Interfacce TypeScript
|
||||||
|
|
||||||
|
### `IElixFormsComponentProperties` (Props)
|
||||||
|
```typescript
|
||||||
|
interface IElixFormsComponentProperties {
|
||||||
|
description: string;
|
||||||
|
isDarkTheme: boolean;
|
||||||
|
environmentMessage: string;
|
||||||
|
hasTeamsContext: boolean;
|
||||||
|
userDisplayName: string;
|
||||||
|
additionalFieldsJson?: string; // JSON schema per campi dinamici
|
||||||
|
customFormFieldsConfiguration?: ( // Callback con factory per campi custom
|
||||||
|
formFieldFactory: IElixFormsComponentCustomFormFieldFactory
|
||||||
|
) => JSX.Element;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IElixFormsComponentFormState` (State)
|
||||||
|
```typescript
|
||||||
|
interface IElixFormsComponentFormState {
|
||||||
|
formData: { [key: string]: any };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `IElixFormsComponentCustomFormFieldFactory` (Form Field Factory)
|
||||||
|
```typescript
|
||||||
|
type IElixFormsComponentCustomFormFieldFactory = {
|
||||||
|
createBooleanInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||||
|
createCheckboxInput(name: string, label: string, options: ElixFormsCheckboxOption[]): JSX.Element;
|
||||||
|
createDropdownInput(name: string, label: string, options: 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: ElixFormsRadioOption[], required?: boolean): JSX.Element;
|
||||||
|
createTextInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||||
|
createTextAreaInput(name: string, label: string, required?: boolean): JSX.Element;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tipi per le Opzioni (`ElixFormsTypes.tsx`)
|
||||||
|
```typescript
|
||||||
|
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
|
||||||
|
type ElixFormsDropdownOption = { value: number; label: string };
|
||||||
|
type ElixFormsRadioOption = { value: number; label: string };
|
||||||
|
```
|
||||||
|
|
||||||
|
## Form Field Factory Pattern
|
||||||
|
|
||||||
|
Le pagine usano il pattern **Custom Form Field Factory** per dichiarare i campi del form:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// In App.jsx della pagina
|
||||||
|
var myElixFormsReact = new ElixForms.ElixFormsReact({
|
||||||
|
// ...props...
|
||||||
|
customFormFieldsConfiguration: (formFieldFactory) => (
|
||||||
|
<>
|
||||||
|
{formFieldFactory.createDropdownInput("COL0015", "Goals", [...options], true)}
|
||||||
|
{formFieldFactory.createTextInput("COL0002", "Campo STRING", true)}
|
||||||
|
{formFieldFactory.createTextAreaInput("COL0003", "Campo TEXTAREA", true)}
|
||||||
|
{formFieldFactory.createBooleanInput("COL0004", "Campo BOOLEAN", true)}
|
||||||
|
{formFieldFactory.createRadioInput("COL0005", "Campo RADIO", [...options], true)}
|
||||||
|
{formFieldFactory.createCheckboxInput("COL0006", "Campo CHECKBOX", [...options])}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tipi di Campo Supportati
|
||||||
|
|
||||||
|
| Metodo Factory | Componente FluentUI | HTML Output |
|
||||||
|
|---|---|---|
|
||||||
|
| `createTextInput` | `TextField` | Text input |
|
||||||
|
| `createTextAreaInput` | `TextField` (multiline) | Textarea |
|
||||||
|
| `createNumberInput` | `TextField` (type=number) | Number input |
|
||||||
|
| `createBooleanInput` | `ChoiceGroup` (Sì/No) | Radio buttons |
|
||||||
|
| `createRadioInput` | `ChoiceGroup` | Radio buttons |
|
||||||
|
| `createCheckboxInput` | `Checkbox` + hidden input | Checkboxes |
|
||||||
|
| `createDropdownInput` | `Dropdown` | Select dropdown |
|
||||||
|
| `createHiddenInput` | Native `<input type="text">` | Hidden field |
|
||||||
|
|
||||||
|
## ElixFormsElement (Wrapper Layout)
|
||||||
|
|
||||||
|
`ElixFormsElement` è una classe che incapsula label + input in un layout a due colonne compatibile con il design system elixForms:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
class ElixFormsElement {
|
||||||
|
constructor(labelElement: ReactElement, inputElement: ReactElement, separator?: string);
|
||||||
|
render(): JSX.Element;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Genera markup con classi CSS specifiche di elixForms:
|
||||||
|
- `.iuFieldContainer` → container del campo
|
||||||
|
- `.attrDisplay_left`, `.attrDisplay_label` → colonna label
|
||||||
|
- `.attrDisplay_right`, `.attrDisplay_input` → colonna input
|
||||||
|
|
||||||
|
## QueryParamHelper
|
||||||
|
|
||||||
|
Utility statica per leggere parametri dalla URL corrente:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class QueryParamHelper {
|
||||||
|
static getCheckedFromQuery(paramName: string): number[]; // Per checkbox (valori separati da virgola)
|
||||||
|
static getOptionFromQuery(paramName: string): number | undefined; // Per radio/dropdown
|
||||||
|
static getBooleanFromQuery(paramName: string): boolean | undefined;// Per boolean
|
||||||
|
static getDecodedTextFromQuery(paramName: string): string | undefined; // Per testo (URI decoded)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usato per pre-popolare i campi form con i valori passati via query string dalla piattaforma elixForms.
|
||||||
|
|
||||||
|
## Campi Obbligatori elixForms
|
||||||
|
|
||||||
|
Il componente abstract gestisce automaticamente questi parametri come hidden inputs:
|
||||||
|
- `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`
|
||||||
|
|
||||||
|
## Note Importanti
|
||||||
|
|
||||||
|
- Lo **state management** attuale usa `React.Component` class-based con `this.state` e `this.setState`
|
||||||
|
- I checkbox gestiscono un `Map<string, number[]>` interno per tracciare i valori selezionati
|
||||||
|
- Il form fa POST a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
|
||||||
|
- L'encoding charset è `ISO-8859-1` per compatibilità con il backend
|
||||||
|
- La libreria usa **SCSS modules** per gli stili (`ElixFormsComponent.module.scss`)
|
||||||
|
- Il package `common` è di tipo `commonjs` (diverso dalle pagine che sono `module`)
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-create-new-page
|
||||||
|
description: >
|
||||||
|
Guida step-by-step per creare una nuova pagina custom nel progetto elixForms.
|
||||||
|
Copre la struttura dei file, la configurazione Vite per single-bundle,
|
||||||
|
il setup TypeScript, l'importazione dei componenti common, e le convenzioni.
|
||||||
|
Attiva questa skill quando devi creare una nuova pagina, duplicare una pagina esistente,
|
||||||
|
o scaffoldare un nuovo micro-progetto nel monorepo.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Creare una Nuova Pagina elixForms Custom
|
||||||
|
|
||||||
|
## Prerequisiti
|
||||||
|
|
||||||
|
- Node.js installato
|
||||||
|
- Dipendenze root installate: `cd react && npm install`
|
||||||
|
|
||||||
|
## Step 1: Scaffolding con Vite
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd react
|
||||||
|
npm create vite@latest <nome-pagina> -- --template react
|
||||||
|
cd <nome-pagina>
|
||||||
|
npm install vite-plugin-css-injected-by-js
|
||||||
|
npm install -D sass-embedded
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Configurare package.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "@elixforms/<nome-pagina>",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vite-plugin-css-injected-by-js": "^5.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^6.0.1",
|
||||||
|
"sass-embedded": "^1.99.0",
|
||||||
|
"vite": "^8.0.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Nota**: React, ReactDOM e FluentUI NON vanno nel package.json della pagina.
|
||||||
|
> Sono nel `react/package.json` root e vengono risolti via hoisting di npm.
|
||||||
|
|
||||||
|
## Step 3: Configurare vite.config.js
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
cssInjectedByJsPlugin()
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
cssCodeSplit: false,
|
||||||
|
rollupOptions: {
|
||||||
|
input: 'src/main.jsx', // o main.tsx se TypeScript
|
||||||
|
output: {
|
||||||
|
codeSplitting: false,
|
||||||
|
manualChunks: undefined,
|
||||||
|
entryFileNames: '<nome-pagina>.js',
|
||||||
|
assetFileNames: '[name].[ext]'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@common": path.resolve(__dirname, "../common"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
fs: {
|
||||||
|
allow: [".."]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 4: Configurare tsconfig.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"references": [
|
||||||
|
{ "path": "../common" }
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "src",
|
||||||
|
"outDir": "dist",
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 5: Creare il file di dichiarazioni globali
|
||||||
|
|
||||||
|
`src/global.d.ts`:
|
||||||
|
```typescript
|
||||||
|
declare module "*.css";
|
||||||
|
declare module "*.svg";
|
||||||
|
declare module "*.png";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 6: Creare l'entry point
|
||||||
|
|
||||||
|
`src/main.jsx` (o `main.tsx`):
|
||||||
|
```jsx
|
||||||
|
import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import './index.css'
|
||||||
|
import App from './App.jsx'
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root')
|
||||||
|
if (!rootElement) throw new Error('Root element not found')
|
||||||
|
|
||||||
|
createRoot(rootElement).render(
|
||||||
|
<StrictMode>
|
||||||
|
<App />
|
||||||
|
</StrictMode>,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: Creare il componente App
|
||||||
|
|
||||||
|
`src/App.jsx` (o `App.tsx`):
|
||||||
|
```jsx
|
||||||
|
import './App.css'
|
||||||
|
import * as ElixForms from '@common/src/ElixFormsComponent';
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
var myElixFormsReact = new ElixForms.ElixFormsReact({
|
||||||
|
description: "Descrizione pagina",
|
||||||
|
isDarkTheme: false,
|
||||||
|
environmentMessage: "",
|
||||||
|
hasTeamsContext: false,
|
||||||
|
userDisplayName: "",
|
||||||
|
customFormFieldsConfiguration: (formFieldFactory) => (
|
||||||
|
<>
|
||||||
|
{/* Aggiungi i tuoi campi qui usando formFieldFactory */}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
var elixFormsReactRender = myElixFormsReact.render();
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<div className="container_12" id="pageBody">
|
||||||
|
<div className="grid_12">
|
||||||
|
<div id="userConsole" className="fe-behaviour">
|
||||||
|
{/* Header, contenuto e footer della pagina */}
|
||||||
|
<div className="grid_12 console_main">
|
||||||
|
<div className="inside">
|
||||||
|
<div className="receipt">
|
||||||
|
<div className="container">
|
||||||
|
<h1>Titolo Pagina</h1>
|
||||||
|
{elixFormsReactRender}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 8: Configurare i CSS
|
||||||
|
|
||||||
|
`src/App.css` — Importa i CSS remoti del design system elixForms:
|
||||||
|
```css
|
||||||
|
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
|
||||||
|
/* ... altri CSS necessari */
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 9: Configurare VS Code (opzionale)
|
||||||
|
|
||||||
|
Aggiungere configurazione di launch e task in `.vscode/launch.json` e `.vscode/tasks.json` seguendo il pattern esistente per `scelta-carriera`.
|
||||||
|
|
||||||
|
## Step 10: Build e Deploy
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Output in `dist/`:
|
||||||
|
- `<nome-pagina>.js` — Singolo bundle JS pronto per il deploy
|
||||||
|
- Eventuali asset (immagini) referenziati
|
||||||
|
|
||||||
|
## Checklist Nuova Pagina
|
||||||
|
|
||||||
|
- [ ] Cartella creata in `react/<nome-pagina>/`
|
||||||
|
- [ ] `package.json` con `"name": "@elixforms/<nome-pagina>"`
|
||||||
|
- [ ] `vite.config.js` con `entryFileNames: '<nome-pagina>.js'`
|
||||||
|
- [ ] `tsconfig.json` estende `../tsconfig.base.json`
|
||||||
|
- [ ] Alias `@common` configurato in vite.config.js
|
||||||
|
- [ ] CSS remoti importati in App.css
|
||||||
|
- [ ] Entry point `main.jsx` con mount su `#root`
|
||||||
|
- [ ] `global.d.ts` con dichiarazioni per moduli CSS/SVG/PNG
|
||||||
|
- [ ] `npm install` eseguito nella cartella della pagina
|
||||||
|
- [ ] `npm run dev` funziona senza errori
|
||||||
|
- [ ] `npm run build` produce singolo bundle in `dist/`
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-css-design-system
|
||||||
|
description: >
|
||||||
|
Strategia CSS e design system del progetto elixForms.
|
||||||
|
Documenta come vengono importati i CSS remoti della piattaforma,
|
||||||
|
il layout a griglia elixForms, le classi CSS specifiche della piattaforma,
|
||||||
|
gli stili SCSS modules nella libreria common, e la struttura visiva delle pagine.
|
||||||
|
Attiva questa skill quando lavori sugli stili, sul layout delle pagine,
|
||||||
|
o devi capire come funzionano le classi CSS del design system elixForms.
|
||||||
|
---
|
||||||
|
|
||||||
|
# CSS e Design System elixForms
|
||||||
|
|
||||||
|
## Strategia CSS
|
||||||
|
|
||||||
|
Il progetto utilizza **tre livelli di stili**:
|
||||||
|
|
||||||
|
1. **CSS remoti** — Design system dell'Università di Parma, caricati via `@import url(...)` da `console-unipr.elixforms.it`
|
||||||
|
2. **SCSS Modules** — Stili specifici dei componenti in `common/` (es. `ElixFormsComponent.module.scss`)
|
||||||
|
3. **CSS locali** — Stili specifici della pagina (es. `App.css`, `index.css`)
|
||||||
|
|
||||||
|
## CSS Remoti (Design System UniPR/elixForms)
|
||||||
|
|
||||||
|
Importati in `App.css` di ogni pagina:
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* CSS principali del design system */
|
||||||
|
@import url('https://console-unipr.elixforms.it/elixFormsCustom/css/elixForms.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_adaptation.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/bootstrap_overrides.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/console_user.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/designitalia_adaptation.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/fonts.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/isipcss_overrides.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/iulib.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/print.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/responsive.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style-bs.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/style.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/design-bs/util.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/plugins/modaldisplay.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/css/themes/blu-italia.css');
|
||||||
|
@import url('https://console-unipr.elixforms.it/rwe2/isipcss/css/grid_fluid.css');
|
||||||
|
```
|
||||||
|
|
||||||
|
> **IMPORTANTE**: Questi CSS sono caricati a runtime dal CDN e **non vengono bundlati** nel JS.
|
||||||
|
> La pagina deve avere connessione internet per visualizzarsi correttamente.
|
||||||
|
|
||||||
|
## Layout della Pagina (Struttura HTML)
|
||||||
|
|
||||||
|
Le pagine seguono un layout standard elixForms con classi CSS specifiche:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="container_12" id="pageBody">
|
||||||
|
<div class="grid_12">
|
||||||
|
<div id="userConsole" class="fe-behaviour">
|
||||||
|
|
||||||
|
<!-- HEADER -->
|
||||||
|
<header>
|
||||||
|
<div class="neutral">
|
||||||
|
<div class="grid_12">
|
||||||
|
<div class="console_header">
|
||||||
|
<div class="leftDiv">
|
||||||
|
<h1>elixForms</h1>
|
||||||
|
<div class="logo"><!-- Logo UniPR --></div>
|
||||||
|
</div>
|
||||||
|
<div class="rightDiv">...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- CONTENUTO PRINCIPALE -->
|
||||||
|
<div class="grid_12 console_main">
|
||||||
|
<div class="inside">
|
||||||
|
<div class="fe-module-header-container">
|
||||||
|
<h2>Titolo Modulo</h2>
|
||||||
|
</div>
|
||||||
|
<div class="receipt">
|
||||||
|
<div class="container">
|
||||||
|
<!-- Contenuto custom della pagina -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- FOOTER -->
|
||||||
|
<footer class="it-footer">
|
||||||
|
<div class="it-footer-main">
|
||||||
|
<div class="container">
|
||||||
|
<div class="subfooter-top">powered by elixForms</div>
|
||||||
|
<div class="subfooter-bottom">versione X.XX.X</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Classi CSS del Design System
|
||||||
|
|
||||||
|
### Layout a Griglia
|
||||||
|
| Classe | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| `container_12` | Container principale a 12 colonne |
|
||||||
|
| `grid_12` | Occupa tutte le 12 colonne |
|
||||||
|
| `clear` | Clearfix |
|
||||||
|
|
||||||
|
### Struttura Pagina
|
||||||
|
| Classe | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| `fe-behaviour` | Container principale della console utente |
|
||||||
|
| `console_header` | Header della console |
|
||||||
|
| `console_main` | Area contenuto principale |
|
||||||
|
| `fe-module-header-container` | Header del modulo/form |
|
||||||
|
| `receipt` | Container del contenuto del form |
|
||||||
|
|
||||||
|
### Layout Campi Form (ElixFormsElement)
|
||||||
|
| Classe | Scopo |
|
||||||
|
|---|---|
|
||||||
|
| `iuFieldContainer` | Container di un campo form (label + input) |
|
||||||
|
| `attrDisplay_left` | Colonna sinistra (label) |
|
||||||
|
| `attrDisplay_right` | Colonna destra (input) |
|
||||||
|
| `attrDisplay_label` | Marcatore per la label |
|
||||||
|
| `attrDisplay_input` | Marcatore per l'input |
|
||||||
|
| `iuLabelContainer` | Container interno della label |
|
||||||
|
| `iuInputContainer` | Container interno dell'input |
|
||||||
|
| `iuTypeString` | Tipo di input stringa |
|
||||||
|
| `iuClearContainer` | Clearfix interno |
|
||||||
|
|
||||||
|
### Classi Input FluentUI Custom
|
||||||
|
| Classe | Componente |
|
||||||
|
|---|---|
|
||||||
|
| `isiportalPartialAdminFormFieldSingleLineText` | TextField singola riga |
|
||||||
|
| `isiportalPartialAdminFormFieldMultiLineText` | TextField multiriga |
|
||||||
|
| `isiportalPartialAdminFormFieldRadio` | ChoiceGroup radio |
|
||||||
|
| `isiportalPartialAdminFormFieldSelect` | Dropdown select |
|
||||||
|
| `isiportalPartialAdminCheckboxFieldItem` | Checkbox singolo |
|
||||||
|
|
||||||
|
## SCSS Modules (common/)
|
||||||
|
|
||||||
|
`ElixFormsComponent.module.scss`:
|
||||||
|
```scss
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Nota**: Gli stili usano il **theming di FluentUI** con variabili CSS e placeholder di tema SharePoint.
|
||||||
|
|
||||||
|
## Logo e Risorse Esterne
|
||||||
|
|
||||||
|
- **Logo UniPR**: `https://console-unipr.elixforms.it/elixFormsCustom/images/logo.png`
|
||||||
|
- Le immagini locali vanno in `src/assets/` e importate come moduli ES
|
||||||
|
- Le immagini vengono copiate nella `dist/` durante la build
|
||||||
|
|
||||||
|
## Note Importanti
|
||||||
|
|
||||||
|
1. Il CSS di `index.css` nella pagina `scelta-carriera` è **attualmente tutto commentato** — si usano solo i CSS remoti
|
||||||
|
2. Il CSS injection plugin (`vite-plugin-css-injected-by-js`) gestisce solo i CSS locali e SCSS, non gli `@import url(...)` che restano come riferimenti esterni
|
||||||
|
3. Per funzionare correttamente, la pagina deployata deve poter raggiungere `console-unipr.elixforms.it`
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-dev-workflow
|
||||||
|
description: >
|
||||||
|
Workflow di sviluppo, debug e deploy del progetto elixForms Custom Pages.
|
||||||
|
Copre i comandi per avviare il dev server, buildare, debuggare con VS Code,
|
||||||
|
e le configurazioni di launch/tasks.
|
||||||
|
Attiva questa skill quando devi eseguire comandi di sviluppo,
|
||||||
|
configurare il debug, o gestire il ciclo dev/build/deploy.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow di Sviluppo
|
||||||
|
|
||||||
|
## Comandi Principali
|
||||||
|
|
||||||
|
Tutti i comandi vanno eseguiti **dalla cartella della pagina** (es. `react/scelta-carriera/`):
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Installare le dipendenze (prima volta o dopo modifiche a package.json)
|
||||||
|
cd react
|
||||||
|
npm install
|
||||||
|
cd scelta-carriera
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Avviare il dev server con HMR
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build per produzione (singolo bundle JS)
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Preview della build di produzione
|
||||||
|
npm run preview
|
||||||
|
|
||||||
|
# Lint del codice
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configurazione VS Code
|
||||||
|
|
||||||
|
### Launch Configurations (`.vscode/launch.json`)
|
||||||
|
|
||||||
|
#### 1. "Scelta Carriere (BROWSER)" — Dev + Chrome Debug
|
||||||
|
- Avvia `npm run dev` nella cartella della pagina
|
||||||
|
- Apre automaticamente Chrome quando il server è pronto
|
||||||
|
- Pattern di rilevamento: `Local:\s+http://localhost:([0-9]+)/`
|
||||||
|
- WebRoot per sourcemaps: `${workspaceFolder}/react/scelta-carriera/src`
|
||||||
|
|
||||||
|
#### 2. "Scelta Carriere (DEBUG ONLY)" — Solo Dev Server
|
||||||
|
- Avvia `npm run dev` senza aprire il browser
|
||||||
|
- Utile per debug da terminale o browser già aperto
|
||||||
|
|
||||||
|
### Variabili d'Ambiente
|
||||||
|
- `NO_COLOR: "1"` — Disabilita colori ANSI nell'output (migliora leggibilità nel debug console)
|
||||||
|
|
||||||
|
### Skip Files nel Debug
|
||||||
|
```json
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/@vite/client/**"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tasks (`.vscode/tasks.json`)
|
||||||
|
|
||||||
|
#### "Start scelta-carriera (Vite App)"
|
||||||
|
- Task background che avvia `npm run dev`
|
||||||
|
- Problem matcher configurato per output Vite:
|
||||||
|
- `beginsPattern`: "VITE"
|
||||||
|
- `endsPattern`: "ready in"
|
||||||
|
|
||||||
|
## Aggiungere una Nuova Pagina al Debug
|
||||||
|
|
||||||
|
Per aggiungere una nuova configurazione di debug per una pagina:
|
||||||
|
|
||||||
|
```json
|
||||||
|
// In .vscode/launch.json, aggiungere in "configurations":
|
||||||
|
{
|
||||||
|
"name": "<Nome Pagina> (BROWSER)",
|
||||||
|
"cwd": "${workspaceFolder}/react/<nome-pagina>",
|
||||||
|
"env": { "NO_COLOR": "1" },
|
||||||
|
"outputCapture": "console",
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"runtimeExecutable": "npm",
|
||||||
|
"runtimeArgs": ["run", "dev"],
|
||||||
|
"skipFiles": [
|
||||||
|
"<node_internals>/**",
|
||||||
|
"**/node_modules/**",
|
||||||
|
"**/@vite/client/**"
|
||||||
|
],
|
||||||
|
"serverReadyAction": {
|
||||||
|
"action": "debugWithChrome",
|
||||||
|
"pattern": "Local:\\s+http://localhost:([0-9]+)/",
|
||||||
|
"uriFormat": "http://localhost:%s",
|
||||||
|
"webRoot": "${workspaceFolder}/react/<nome-pagina>/src"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flusso di Deploy
|
||||||
|
|
||||||
|
1. `npm run build` nella cartella della pagina
|
||||||
|
2. Output: `dist/<nome-pagina>.js` (+ sourcemap e asset)
|
||||||
|
3. Caricare il file JS sul server remoto `console-unipr.elixforms.it`
|
||||||
|
4. La pagina viene servita dalla piattaforma elixForms con i parametri query appropriati
|
||||||
|
|
||||||
|
## Testing Locale con Query Parameters
|
||||||
|
|
||||||
|
Per testare localmente con i parametri elixForms, aggiungere query params all'URL del dev server:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:5173/?RWE2_MODULE_ID=123&RWE2_REQUEST_ID=456&COL0002=test&COL0015=3
|
||||||
|
```
|
||||||
|
|
||||||
|
I valori verranno letti da `QueryParamHelper` e pre-popoleranno i campi del form.
|
||||||
|
|
||||||
|
## Struttura delle Dipendenze
|
||||||
|
|
||||||
|
```
|
||||||
|
react/ ← npm install qui per dipendenze condivise
|
||||||
|
├── node_modules/ ← react, react-dom, @fluentui/react
|
||||||
|
├── package.json
|
||||||
|
└── scelta-carriera/ ← npm install qui per devDependencies
|
||||||
|
├── node_modules/ ← vite, eslint, plugin, sass-embedded
|
||||||
|
└── package.json
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Nota**: Le dipendenze runtime (React, FluentUI) sono nel `package.json` root.
|
||||||
|
> Le dipendenze di sviluppo (Vite, ESLint) sono nel `package.json` della pagina.
|
||||||
|
> npm risolve le dipendenze risalendo la gerarchia delle cartelle (hoisting).
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-project-architecture
|
||||||
|
description: >
|
||||||
|
Architettura e struttura del progetto elixForms Custom Pages.
|
||||||
|
Descrive il monorepo, la libreria common condivisa, il pattern delle pagine autonome
|
||||||
|
e tutte le convenzioni di naming, configurazione e build.
|
||||||
|
Attiva questa skill quando lavori su qualsiasi parte del progetto,
|
||||||
|
crei nuove pagine, modifichi l'architettura o devi capire come è organizzato il codice.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Architettura del Progetto elixForms Custom Pages
|
||||||
|
|
||||||
|
## Panoramica
|
||||||
|
|
||||||
|
Questo progetto è un **monorepo** per la creazione di pagine web React custom destinate alla piattaforma **elixForms** dell'Università di Parma (UniPR). Ogni pagina è un micro-progetto indipendente che produce un **singolo file JS bundle** contenente React, Fluent UI, CSS e il codice custom, da pubblicare su un server remoto.
|
||||||
|
|
||||||
|
## Struttura del Workspace
|
||||||
|
|
||||||
|
```
|
||||||
|
elixforms-custom-pages/ ← Root del workspace (VS Code)
|
||||||
|
├── .vscode/ ← Configurazioni VS Code (launch, tasks)
|
||||||
|
├── docs/ ← Documentazione (PDF manuali)
|
||||||
|
└── react/ ← Root del monorepo React
|
||||||
|
├── package.json ← Dipendenze condivise (React, ReactDOM, FluentUI)
|
||||||
|
├── tsconfig.base.json ← Configurazione TypeScript base condivisa
|
||||||
|
├── .gitignore
|
||||||
|
├── common/ ← Libreria condivisa di componenti ElixForms
|
||||||
|
│ ├── package.json
|
||||||
|
│ ├── tsconfig.json ← Estende tsconfig.base, composite: true
|
||||||
|
│ └── src/
|
||||||
|
│ ├── ElixFormsComponent.tsx
|
||||||
|
│ ├── ElixFormsComponentAbstract.tsx
|
||||||
|
│ ├── ElixFormsElement.tsx
|
||||||
|
│ ├── ElixFormsTypes.tsx
|
||||||
|
│ ├── ElixFormsComponent.module.scss
|
||||||
|
│ ├── IElixFormsComponentProperties.ts
|
||||||
|
│ ├── IElixFormsComponentFormState.tsx
|
||||||
|
│ ├── IElixFormsComponentCustomFormFieldFactory.ts
|
||||||
|
│ └── QueryParamHelper.tsx
|
||||||
|
└── scelta-carriera/ ← Esempio di pagina custom (micro-progetto)
|
||||||
|
├── package.json
|
||||||
|
├── tsconfig.json ← Estende tsconfig.base
|
||||||
|
├── vite.config.js
|
||||||
|
├── eslint.config.js
|
||||||
|
├── index.html
|
||||||
|
└── src/
|
||||||
|
├── main.jsx ← Entry point React
|
||||||
|
├── App.jsx ← Componente principale della pagina
|
||||||
|
├── App.css ← CSS (importa CSS remoti di elixForms)
|
||||||
|
├── index.css ← CSS globale (attualmente commentato)
|
||||||
|
├── global.d.ts ← Dichiarazioni di tipo per moduli
|
||||||
|
└── assets/ ← Immagini e risorse statiche
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern Architetturale: Micro-Progetto per Pagina
|
||||||
|
|
||||||
|
Ogni pagina (es. `scelta-carriera/`) è un progetto Vite completamente autonomo che:
|
||||||
|
|
||||||
|
1. **Importa dalla libreria `common/`** tramite alias `@common` configurato in `vite.config.js`
|
||||||
|
2. **Ha le proprie dipendenze** nel suo `package.json` (solo devDependencies e plugin Vite)
|
||||||
|
3. **Condivide le dipendenze runtime** (React, ReactDOM, FluentUI) dal `package.json` root di `react/`
|
||||||
|
4. **Produce un singolo file JS** tramite la configurazione Rollup in `vite.config.js`
|
||||||
|
5. **Estende il tsconfig base** per avere configurazione TypeScript coerente
|
||||||
|
|
||||||
|
## Dipendenze Condivise (react/package.json)
|
||||||
|
|
||||||
|
Le dipendenze runtime sono hoisted al livello `react/`:
|
||||||
|
- `react` ^19.2.7
|
||||||
|
- `react-dom` ^19.2.5
|
||||||
|
- `@fluentui/react` ^8.125.6
|
||||||
|
|
||||||
|
## Configurazione TypeScript
|
||||||
|
|
||||||
|
### Base (tsconfig.base.json)
|
||||||
|
- `module`: ESNext
|
||||||
|
- `target`: ES2022
|
||||||
|
- `moduleResolution`: bundler
|
||||||
|
- `jsx`: react-jsx
|
||||||
|
- `strict`: true
|
||||||
|
- Path alias: `@common/*` → `./common/*`
|
||||||
|
|
||||||
|
### Common (common/tsconfig.json)
|
||||||
|
- `composite`: true (per project references)
|
||||||
|
- `declaration`: true, `declarationMap`: true
|
||||||
|
|
||||||
|
### Pagine (scelta-carriera/tsconfig.json)
|
||||||
|
- `extends`: `../tsconfig.base.json`
|
||||||
|
- `references`: `../common`
|
||||||
|
- `allowJs`: true, `checkJs`: true (supporto misto JSX/TSX)
|
||||||
|
|
||||||
|
## Configurazione Build (Vite)
|
||||||
|
|
||||||
|
Ogni pagina usa questa configurazione per generare un **singolo bundle JS**:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Plugin chiave
|
||||||
|
react() // Supporto React JSX
|
||||||
|
cssInjectedByJsPlugin() // CSS iniettato nel JS (no file .css separati)
|
||||||
|
|
||||||
|
// Build options
|
||||||
|
cssCodeSplit: false
|
||||||
|
rollupOptions.input: 'src/main.jsx'
|
||||||
|
rollupOptions.output:
|
||||||
|
codeSplitting: false
|
||||||
|
manualChunks: undefined
|
||||||
|
entryFileNames: '<nome-pagina>.js' // Nome descrittivo (es. 'scelta-carriera.js')
|
||||||
|
assetFileNames: '[name].[ext]'
|
||||||
|
sourcemap: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resolve Alias
|
||||||
|
|
||||||
|
```js
|
||||||
|
resolve.alias: {
|
||||||
|
"@common": path.resolve(__dirname, "../common")
|
||||||
|
}
|
||||||
|
server.fs.allow: [".."] // Permette import da common/
|
||||||
|
```
|
||||||
|
|
||||||
|
## CSS Strategy
|
||||||
|
|
||||||
|
Le pagine caricano CSS dal server remoto elixForms tramite `@import url(...)` in `App.css`:
|
||||||
|
- CSS da `console-unipr.elixforms.it` per il design system dell'università
|
||||||
|
- I componenti FluentUI usano stili inline/theme nativi
|
||||||
|
- SCSS modules sono usati in `common/` per stili dei componenti condivisi
|
||||||
|
- `sass-embedded` è installato come devDependency per supporto SCSS
|
||||||
|
|
||||||
|
## Integrazione con elixForms
|
||||||
|
|
||||||
|
La pagina custom interagisce con la piattaforma elixForms tramite:
|
||||||
|
1. **Query parameters**: Parametri obbligatori passati nell'URL (es. `RWE2_MODULE_ID`, `RWE2_REQUEST_ID`, `crc`, etc.)
|
||||||
|
2. **Form POST**: Il form fa submit a `https://procedure.unipr.it/rwe2/ComeBackToElixAndSave`
|
||||||
|
3. **Hidden inputs**: I parametri query vengono inseriti come campi hidden nel form
|
||||||
|
4. **Encoding**: `acceptCharset="ISO-8859-1"` per compatibilità con il backend
|
||||||
|
|
||||||
|
## Convenzioni di Naming
|
||||||
|
|
||||||
|
- **Cartelle pagina**: kebab-case (es. `scelta-carriera`)
|
||||||
|
- **Package name**: `@elixforms/<nome-pagina>` (es. `@elixforms/scelta-carriera`)
|
||||||
|
- **Bundle output**: `<nome-pagina>.js` (es. `scelta-carriera.js`)
|
||||||
|
- **Componenti React**: PascalCase
|
||||||
|
- **File sorgente**: I file correnti sono `.jsx` ma il progetto ha pieno supporto TypeScript (`.tsx`)
|
||||||
|
- **Tipo moduli**: `"type": "module"` nelle pagine
|
||||||
|
|
||||||
|
## Ambiente di Sviluppo
|
||||||
|
|
||||||
|
- **Dev server**: `npm run dev` (Vite HMR)
|
||||||
|
- **Build**: `npm run build` → output in `dist/`
|
||||||
|
- **Debug VS Code**: Configurazioni in `.vscode/launch.json` per avviare Vite + Chrome
|
||||||
|
- **Lint**: ESLint con plugin react-hooks e react-refresh
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-typescript-conventions
|
||||||
|
description: >
|
||||||
|
Convenzioni TypeScript e React per il progetto elixForms.
|
||||||
|
Copre la configurazione TypeScript, i pattern di codice, la gestione dei tipi,
|
||||||
|
l'uso di JSDoc per type annotations nei file .jsx, e le best practices.
|
||||||
|
Attiva questa skill quando scrivi codice TypeScript o React,
|
||||||
|
crei nuovi componenti, definisci interfacce, o gestisci i tipi nel progetto.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Convenzioni TypeScript e React
|
||||||
|
|
||||||
|
## Configurazione TypeScript del Progetto
|
||||||
|
|
||||||
|
### Opzioni Compiler Principali
|
||||||
|
- **target**: ES2022
|
||||||
|
- **module**: ESNext
|
||||||
|
- **moduleResolution**: bundler
|
||||||
|
- **jsx**: react-jsx (trasformazione automatica JSX senza import React)
|
||||||
|
- **strict**: true
|
||||||
|
- **isolatedModules**: true
|
||||||
|
- **verbatimModuleSyntax**: true (usa `import type` per import solo di tipi)
|
||||||
|
|
||||||
|
### File Misti JSX/TSX
|
||||||
|
|
||||||
|
Il progetto supporta **sia file `.jsx` che `.tsx`** grazie a:
|
||||||
|
- `allowJs: true` e `checkJs: true` nel tsconfig delle pagine
|
||||||
|
- Type annotations via JSDoc nei file `.jsx`
|
||||||
|
|
||||||
|
#### Pattern JSDoc per Tipi nei File .jsx
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
/** @type {import('@common/src/ElixFormsComponent').ElixFormsReact} */
|
||||||
|
var myElixFormsReact = new ElixForms.ElixFormsReact({...});
|
||||||
|
|
||||||
|
// Per i parametri di callback
|
||||||
|
customFormFieldsConfiguration: (
|
||||||
|
/** @type {import('../../common/src/IElixFormsComponentCustomFormFieldFactory').IElixFormsComponentCustomFormFieldFactory} */
|
||||||
|
formFieldFactory) => (...)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern React Correnti
|
||||||
|
|
||||||
|
### Class Components (common/)
|
||||||
|
|
||||||
|
La libreria `common/` usa **React Class Components** (non hooks):
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
abstract class ElixFormsComponentAbstract
|
||||||
|
extends Component<IElixFormsComponentProperties, IElixFormsComponentFormState> {
|
||||||
|
|
||||||
|
constructor(props: IElixFormsComponentProperties) {
|
||||||
|
super(props);
|
||||||
|
this.state = { formData: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
// State gestito con this.setState()
|
||||||
|
this.setState({ formData: { ...this.state.formData, [name]: value } });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functional Components (pagine)
|
||||||
|
|
||||||
|
Le pagine usano **Functional Components** con hooks:
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [count, setCount] = useState(0)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Istanziazione del Componente ElixForms
|
||||||
|
|
||||||
|
Il componente ElixForms viene istanziato come classe (non come JSX):
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
// ✅ Corretto - istanziazione diretta
|
||||||
|
var myElixFormsReact = new ElixForms.ElixFormsReact({...props...});
|
||||||
|
var elixFormsReactRender = myElixFormsReact.render();
|
||||||
|
|
||||||
|
// Poi nel JSX
|
||||||
|
return <>{elixFormsReactRender}</>;
|
||||||
|
|
||||||
|
// ❌ NON usare come componente JSX
|
||||||
|
return <ElixForms.ElixFormsReact {...props} />;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Import Conventions
|
||||||
|
|
||||||
|
### Import Type-Only (verbatimModuleSyntax)
|
||||||
|
|
||||||
|
Con `verbatimModuleSyntax: true`, usare sempre `import type` per i tipi:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// ✅ Corretto
|
||||||
|
import type { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
|
||||||
|
import type { JSX } from 'react';
|
||||||
|
|
||||||
|
// ❌ Errato - causerà errore con verbatimModuleSyntax
|
||||||
|
import { IElixFormsComponentProperties } from './IElixFormsComponentProperties';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Import di Moduli
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Fluent UI - import tutto il namespace
|
||||||
|
import * as FluentUI from '@fluentui/react';
|
||||||
|
|
||||||
|
// React - import specifici
|
||||||
|
import { Component, type FormEvent, type JSX, useState } from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
// Componenti common - import namespace
|
||||||
|
import * as ElixForms from '@common/src/ElixFormsComponent';
|
||||||
|
|
||||||
|
// CSS
|
||||||
|
import './App.css'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tipi Custom del Progetto
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Opzioni per campi form
|
||||||
|
type ElixFormsCheckboxOption = { value: number; label: string; required?: boolean };
|
||||||
|
type ElixFormsDropdownOption = { value: number; label: string };
|
||||||
|
type ElixFormsRadioOption = { value: number; label: string };
|
||||||
|
|
||||||
|
// State del form
|
||||||
|
interface IElixFormsComponentFormState {
|
||||||
|
formData: { [key: string]: any };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dichiarazioni di Modulo
|
||||||
|
|
||||||
|
`global.d.ts` nella cartella `src/` di ogni pagina:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
declare module "*.css";
|
||||||
|
declare module "*.svg";
|
||||||
|
declare module "*.png";
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices per il Progetto
|
||||||
|
|
||||||
|
1. **Naming**:
|
||||||
|
- Interfacce: prefisso `I` (es. `IElixFormsComponentProperties`)
|
||||||
|
- Tipi: PascalCase (es. `ElixFormsCheckboxOption`)
|
||||||
|
- File interfacce: nome dell'interfaccia (es. `IElixFormsComponentProperties.ts`)
|
||||||
|
- File componenti: nome del componente (es. `ElixFormsComponent.tsx`)
|
||||||
|
|
||||||
|
2. **Estensioni file**:
|
||||||
|
- `.tsx` per componenti React con TypeScript (common/)
|
||||||
|
- `.ts` per moduli TypeScript puri (interfacce, tipi)
|
||||||
|
- `.jsx` per componenti React con JavaScript (pagine attuali)
|
||||||
|
- `.scss` per stili con moduli SCSS
|
||||||
|
|
||||||
|
3. **Export**:
|
||||||
|
- Named export per tipi e interfacce
|
||||||
|
- Default export per classi componente
|
||||||
|
- Re-export con alias: `export { ElixFormsComponent as ElixFormsReact }`
|
||||||
|
|
||||||
|
4. **State management**:
|
||||||
|
- Usare `this.setState` con spread operator per update immutabili
|
||||||
|
- I checkbox mantengono un `Map<string, number[]>` separato
|
||||||
|
|
||||||
|
5. **Fluent UI**:
|
||||||
|
- Usare Fluent UI React v8 (`@fluentui/react`)
|
||||||
|
- Componenti: `TextField`, `Checkbox`, `ChoiceGroup`, `Dropdown`, `Label`, `Stack`
|
||||||
|
- Le opzioni radio usano `IChoiceGroupOption[]`
|
||||||
|
- Le opzioni dropdown usano `IDropdownOption[]`
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
---
|
||||||
|
name: elixforms-vite-build-config
|
||||||
|
description: >
|
||||||
|
Configurazione Vite e Rollup per il progetto elixForms.
|
||||||
|
Copre la build single-bundle, i plugin utilizzati, gli alias,
|
||||||
|
il sourcemap, e la strategia CSS (injection nel JS).
|
||||||
|
Attiva questa skill quando devi modificare la build configuration,
|
||||||
|
risolvere problemi di bundling, aggiungere plugin Vite,
|
||||||
|
o ottimizzare l'output di produzione.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configurazione Vite e Build
|
||||||
|
|
||||||
|
## Stack di Build
|
||||||
|
|
||||||
|
- **Vite** v8.x — Dev server e bundler
|
||||||
|
- **@vitejs/plugin-react** v6.x — Supporto React/JSX
|
||||||
|
- **vite-plugin-css-injected-by-js** v5.x — Inietta CSS nel bundle JS
|
||||||
|
- **sass-embedded** v1.99.x — Compilazione SCSS (per common/)
|
||||||
|
- **Rollup** — Bundler interno di Vite (configurato via `rollupOptions`)
|
||||||
|
|
||||||
|
## Configurazione Vite Completa di Riferimento
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
cssInjectedByJsPlugin()
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
outDir: 'dist',
|
||||||
|
cssCodeSplit: false,
|
||||||
|
rollupOptions: {
|
||||||
|
input: 'src/main.jsx',
|
||||||
|
output: {
|
||||||
|
codeSplitting: false,
|
||||||
|
manualChunks: undefined,
|
||||||
|
entryFileNames: '<nome-pagina>.js',
|
||||||
|
assetFileNames: '[name].[ext]'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@common": path.resolve(__dirname, "../common"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
fs: {
|
||||||
|
allow: [".."]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dettaglio Opzioni Critiche
|
||||||
|
|
||||||
|
### Single Bundle (Rollup)
|
||||||
|
| Opzione | Valore | Scopo |
|
||||||
|
|---|---|---|
|
||||||
|
| `codeSplitting` | `false` | Disabilita il code splitting |
|
||||||
|
| `manualChunks` | `undefined` | Impedisce separazione vendor/app |
|
||||||
|
| `entryFileNames` | `'<nome>.js'` | Nome deterministico senza hash |
|
||||||
|
| `assetFileNames` | `'[name].[ext]'` | Asset senza hash nei nomi |
|
||||||
|
|
||||||
|
### CSS Strategy
|
||||||
|
| Opzione | Valore | Scopo |
|
||||||
|
|---|---|---|
|
||||||
|
| `cssCodeSplit` | `false` | No file CSS separati |
|
||||||
|
| `cssInjectedByJsPlugin()` | plugin | CSS iniettato runtime nel DOM via JS |
|
||||||
|
|
||||||
|
### Alias di Import
|
||||||
|
| Alias | Path | Scopo |
|
||||||
|
|---|---|---|
|
||||||
|
| `@common` | `../common` | Accesso alla libreria condivisa |
|
||||||
|
|
||||||
|
### Dev Server
|
||||||
|
| Opzione | Valore | Scopo |
|
||||||
|
|---|---|---|
|
||||||
|
| `server.fs.allow` | `[".."]` | Permette import fuori dalla root del progetto |
|
||||||
|
|
||||||
|
## Comandi
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Dev con HMR
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# Build produzione
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Preview build produzione
|
||||||
|
npm run preview
|
||||||
|
|
||||||
|
# Lint
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output della Build
|
||||||
|
|
||||||
|
```
|
||||||
|
dist/
|
||||||
|
<nome-pagina>.js ← Bundle unico (React + FluentUI + CSS + codice)
|
||||||
|
<nome-pagina>.js.map ← Source map
|
||||||
|
*.png, *.svg ← Asset referenziati nel codice
|
||||||
|
```
|
||||||
|
|
||||||
|
## Risoluzione Problemi Comuni
|
||||||
|
|
||||||
|
### Errore: "The request url is outside of Vite serving allow list"
|
||||||
|
- **Causa**: Import di file dalla cartella `common/` o altre cartelle esterne
|
||||||
|
- **Fix**: Aggiungere `server.fs.allow: [".."]` nella configurazione Vite
|
||||||
|
|
||||||
|
### Errore: "Cannot find module '@common/...'"
|
||||||
|
- **Causa**: Alias non configurato o path errato
|
||||||
|
- **Fix**: Verificare `resolve.alias` e che la cartella `common/` esista
|
||||||
|
|
||||||
|
### Bundle troppo grande
|
||||||
|
- **Causa**: FluentUI include molti componenti
|
||||||
|
- **Fix possibili**:
|
||||||
|
- Tree shaking (import specifici: `import { TextField } from '@fluentui/react'`)
|
||||||
|
- Considerare `@fluentui/react-components` (v9) per bundle più piccoli
|
||||||
|
- Esternalizzare React se la pagina host lo fornisce già
|
||||||
|
|
||||||
|
### CSS non applicato in produzione
|
||||||
|
- **Causa**: `cssInjectedByJsPlugin` non installato o non configurato
|
||||||
|
- **Fix**: Verificare plugin in `vite.config.js` e `npm install vite-plugin-css-injected-by-js`
|
||||||
|
|
||||||
|
### SCSS non compilato
|
||||||
|
- **Causa**: `sass-embedded` mancante
|
||||||
|
- **Fix**: `npm install -D sass-embedded`
|
||||||
Binary file not shown.
Generated
+3
-2
@@ -262,14 +262,14 @@
|
|||||||
"version": "3.2.3",
|
"version": "3.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.2.7",
|
"version": "19.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
|
||||||
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
|
"integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -279,6 +279,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz",
|
||||||
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
|
"integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+42
-24
@@ -54,6 +54,7 @@
|
|||||||
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.29.0",
|
"@babel/code-frame": "^7.29.0",
|
||||||
"@babel/generator": "^7.29.0",
|
"@babel/generator": "^7.29.0",
|
||||||
@@ -270,31 +271,10 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/core": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@emnapi/wasi-threads": "1.2.1",
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/runtime": {
|
|
||||||
"version": "1.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
|
||||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.4.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@emnapi/wasi-threads": {
|
"node_modules/@emnapi/wasi-threads": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
|
||||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1078,6 +1058,37 @@
|
|||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/wasi-threads": "1.2.1",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||||
|
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||||
"version": "1.0.0-rc.17",
|
"version": "1.0.0-rc.17",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz",
|
||||||
@@ -1154,6 +1165,7 @@
|
|||||||
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.2.2"
|
"csstype": "^3.2.2"
|
||||||
}
|
}
|
||||||
@@ -1200,6 +1212,7 @@
|
|||||||
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
},
|
},
|
||||||
@@ -1290,6 +1303,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.10.12",
|
"baseline-browser-mapping": "^2.10.12",
|
||||||
"caniuse-lite": "^1.0.30001782",
|
"caniuse-lite": "^1.0.30001782",
|
||||||
@@ -1447,6 +1461,7 @@
|
|||||||
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
|
"integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eslint-community/eslint-utils": "^4.8.0",
|
"@eslint-community/eslint-utils": "^4.8.0",
|
||||||
"@eslint-community/regexpp": "^4.12.2",
|
"@eslint-community/regexpp": "^4.12.2",
|
||||||
@@ -2487,6 +2502,7 @@
|
|||||||
"integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==",
|
"integrity": "sha512-gF/juR1aX02lZHkvwxdF80SapkQeg2fetoDF6gIQkNbSw5YEUFspMkyGTjPjgZSgIHuZpy+Wz4PlebKnLXMjdg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^2.5.0",
|
"@bufbuild/protobuf": "^2.5.0",
|
||||||
"colorjs.io": "^0.5.0",
|
"colorjs.io": "^0.5.0",
|
||||||
@@ -2981,6 +2997,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz",
|
||||||
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
"integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lightningcss": "^1.32.0",
|
"lightningcss": "^1.32.0",
|
||||||
"picomatch": "^4.0.4",
|
"picomatch": "^4.0.4",
|
||||||
@@ -3114,6 +3131,7 @@
|
|||||||
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user