Salta ai contenuti

Sistema dei Segnaposto

Il sistema dei segnaposto consente ai lettori di personalizzare indirizzi IP, ASN e altri valori specifici del deployment in tutta la documentazione. Gli autori inseriscono token nel proprio Markdown; il browser li sostituisce con i valori forniti dall’utente a runtime.

I token seguono il pattern regex:

x([A-Z][A-Z0-9_]+)x

Un token inizia e termina con una x minuscola e contiene un identificatore maiuscolo. Ad esempio, xCUSTOMER_ASNx fa riferimento al segnaposto CUSTOMER_ASN.

La regex è definita in src/scripts/placeholder-dom.ts:

const PH_REGEX = /x([A-Z][A-Z0-9_]+)x/g;

Tutti i segnaposto sono dichiarati in src/data/placeholders.json. Ogni voce ha questa struttura:

{
"CUSTOMER_ASN": {
"type": "text",
"default": "64496",
"description": "Your public ASN (registered with ARIN/RIR)"
}
}
CampoObbligatorioDescrizione
type"text" per input libero, "dropdown" per menu a tendina
defaultValore iniziale mostrato prima che il lettore effettui modifiche
descriptionEtichetta visualizzata nel modulo
optionssolo per dropdownArray di valori consentiti

src/lib/placeholder-store.ts gestisce tutto lo stato dei segnaposto.

I valori sono persistiti in localStorage sotto la chiave f5xc-placeholders. Lo store espone quattro funzioni:

FunzioneScopo
getDefaults()Restituisce una mappa di ogni chiave segnaposto al suo valore default dal JSON
loadValues()Legge da localStorage, ricade su getDefaults()
saveValues(values)Scrive la mappa corrente in localStorage
clearValues()Rimuove la voce da localStorage

FIELD_GROUPS organizza le chiavi dei segnaposto in sezioni etichettate per l’interfaccia del modulo:

export const FIELD_GROUPS: FieldGroup[] = [
{ label: 'Data Center & Scrubbing Centers', keys: ['DC_NAME', 'CENTER_1', 'CENTER_2'] },
{ label: 'Protected Prefixes', keys: ['PROTECTED_CIDR_V4', 'PROTECTED_NET_V4', ...] },
{ label: 'BGP', keys: ['CUSTOMER_ASN', 'F5_XC_ASN', 'BGP_PASSWORD'] },
// ... altri gruppi
];

Alcuni valori sono derivati dall’input dell’utente piuttosto che inseriti direttamente. getComputedValues() li calcola da tabelle di lookup:

const cidrToMask: Record<string, string> = {
'/24 (256 IPs)': '255.255.255.0',
'/23 (512 IPs)': '255.255.254.0',
// ...
};

Vengono prodotti due segnaposto calcolati:

Chiave CalcolataDerivata DaEsempio
PROTECTED_MASK_V4PROTECTED_CIDR_V4 tramite lookup cidrToMask255.255.255.0
PROTECTED_PREFIX_V4PROTECTED_NET_V4 + PROTECTED_CIDR_V4 tramite cidrToShort192.0.2.0/24

getAllValues() unisce i valori inseriti dall’utente con i valori calcolati, fornendo una mappa completa per la sostituzione.

emitChange() invia un CustomEvent placeholder-change su document con la mappa completa dei valori come detail:

export function emitChange(values: Record<string, string>) {
document.dispatchEvent(
new CustomEvent('placeholder-change', { detail: getAllValues(values) }),
);
}

Questo evento guida sia gli aggiornamenti degli span nel DOM sia il re-rendering dei diagrammi Mermaid.

src/components/PlaceholderForm.tsx fornisce l’interfaccia di modifica.

  • Stato: useState inizializzato da loadValues()
  • Al montaggio: useEffect chiama emitChange() per attivare la sostituzione iniziale nel DOM
  • handleChange: Aggiorna lo stato React, chiama saveValues() ed emitChange()
  • handleReset: Chiama clearValues(), ripristina lo stato a getDefaults(), emette il cambio
  • Rendering: Itera FIELD_GROUPS, renderizzando un <fieldset> per gruppo. Ogni chiave ottiene un <input> (tipo text) o un <select> (tipo dropdown)
  • Layout: Il modulo è racchiuso in un elemento <details>, compresso per impostazione predefinita

src/components/PlaceholderFormWrapper.astro connette il componente React alla pagina Astro:

<PlaceholderForm client:only="react" />
<script>
import '../scripts/placeholder-dom.ts';
</script>

client:only="react" indica ad Astro di idratare il componente esclusivamente lato client (nessun SSR). Il tag <script> importa il walker del DOM affinché venga eseguito su ogni pagina che include questo wrapper.

Il wrapper inietta anche CSS globale per lo stile del modulo (.ph-form-wrapper, .ph-grid, .ph-value, ecc.).

src/scripts/placeholder-dom.ts gestisce la sostituzione dei token lato client.

Al caricamento della pagina, viene eseguito init():

  1. Seleziona .sl-markdown-content come radice (ricade su document.body)
  2. Chiama walkTextNodes(root, values) che utilizza document.createTreeWalker con NodeFilter.SHOW_TEXT
  3. Per ogni nodo di testo che corrisponde alla regex del token, lo divide in un document fragment di nodi di testo semplici ed elementi <span data-ph="KEY" class="ph-value">
  4. Sostituisce il nodo di testo originale con il fragment

Dopo la scansione, il DOM contiene span con attributi data-ph al posto dei token grezzi.

Quando il modulo emette un evento placeholder-change, viene eseguito updateSpans():

document.querySelectorAll<HTMLSpanElement>('span[data-ph]').forEach((span) => {
const name = span.getAttribute('data-ph')!;
if (values[name] !== undefined) {
span.textContent = values[name];
}
});

Questo evita di riscansionare l’albero — aggiorna direttamente il contenuto testuale degli span.

Lo script registra due listener:

EventoHandlerScopo
placeholder-changehandleChangeAggiorna gli span e ri-renderizza i diagrammi Mermaid
astro:page-loadinitRiscansiona il DOM dopo la navigazione lato client di Astro
  1. Aggiungere la voce JSON in src/data/placeholders.json:

    "MY_NEW_VALUE": {
    "type": "text",
    "default": "example",
    "description": "Description shown in the form"
    }
  2. Aggiungere la chiave a un gruppo di campi in src/lib/placeholder-store.ts. Aggiungerla all’array keys di un gruppo esistente o creare un nuovo gruppo in FIELD_GROUPS.

  3. Utilizzare il token nel contenuto: Scrivere xMY_NEW_VALUEx in qualsiasi file .mdx. Il walker del DOM lo sostituirà a runtime.

I valori calcolati sono derivati da altri segnaposto. Per aggiungerne uno:

  1. Aggiungere una tabella di lookup (se necessario) in src/lib/placeholder-store.ts, seguendo il pattern di cidrToMask.

  2. Estendere getComputedValues() per includere la nuova chiave derivata:

    export function getComputedValues(values: Record<string, string>): Record<string, string> {
    // ... logica esistente
    return {
    PROTECTED_MASK_V4: mask,
    PROTECTED_PREFIX_V4: `${net}${short}`,
    MY_COMPUTED: derivedValue, // aggiungere qui
    };
    }
  3. Utilizzare xMY_COMPUTEDx nel contenuto come qualsiasi altro token. I valori calcolati non necessitano di una voce in placeholders.json né di un gruppo di campi — sono invisibili al modulo.