@italia/video 0.1.0-alpha.2 → 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,14 +5,14 @@ This webcomponent follows the [open-wc](https://github.com/open-wc/open-wc) reco
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm i it-video
8
+ npm i @italia/video
9
9
  ```
10
10
 
11
11
  ## Usage
12
12
 
13
13
  ```html
14
14
  <script type="module">
15
- import '@italia/it-video';
15
+ import '@italia/video';
16
16
  </script>
17
17
 
18
18
  <it-video></it-video>
@@ -327,7 +327,7 @@
327
327
  {
328
328
  "kind": "variable",
329
329
  "name": "meta",
330
- "default": "{ title: 'Componenti/Video', tags: ['autodocs'], component: 'it-video', args: { src: 'https://vjs.zencdn.net/v/oceans.webm', poster: '', type: 'video/mp4', options: undefined, track: [], consentOptions: {}, lang: 'it', translations: { it: itLang as any }, initPlugins: '', }, argTypes: { src: { control: 'text', description: 'Sorgente del video' }, poster: { control: 'text', description: \"Sorgente dell'immagine di anteprima\" }, type: { control: 'text', description: 'Tipo del video.', table: { defaultValue: { summary: 'video/mp4' } } }, options: { control: 'object', description: 'Opzioni per il video player. https://videojs.com/guides/options/ qui tutte le opzioni disponibili.', }, track: { control: 'text', table: { defaultValue: { summary: '[{kind:\"chapter\", src: \"/path/file.ext\", srclang:\"it\", label: \"Capitoli\", default: true }]', }, }, description: 'Tracce per didascalie, sottotitoli, capitoli e descrizioni. Nel campo `kind` è necessario indicare la tipologia di traccia fra <ul><li>captions</li><li>subtitles</li><li>description</li><li>chapters</li><li>metadata</li></ul>', }, consentOptions: { name: 'consent-options', control: 'object', description: 'Oggetto per la configurazione del consenso dei cookie. <br/>Di default viene salvata una variabile nel localstorage con lo stesso nome del type del video, ma è possibile personalizzarla passando in `consentOptions` un valore per `consentKey`. <br/>Inoltre, quando viene dato il consenso permanente per i cookie, è possibile personalizzare il comportamento passando in questo oggetto due funzioni specifiche per la gestione della memorizzazione del consenso: `onAccept` e `isAccepted`.', table: { defaultValue: { summary: '{}', }, }, }, lang: { control: 'select', options: ['it', 'en', 'fr', 'de', 'es'], description: \"Lingua del player di cui verrano usate le corrispondenti 'translations'\", table: { defaultValue: { summary: 'it' } }, }, translations: { control: 'object', description: 'Traduzioni del player per le diverse lingue. Di base è disponibile solo la lingua it. Usare questa prop per aggiungere le traduzioni in altre lingue. ', }, initPlugins: { name: 'init-plugins', control: 'text', description: 'Nome della propria funzione presente nella window che verrà invocata da video.js per inizializzare eventuali plugin aggiuntivi definiti dallo sviluppatore.', }, }, decorators: [(Story) => html`<div class=\"sbdocs-video-container\">${Story()}</div>`], parameters: { docs: { source: { excludeDecorators: true }, description: { component: ` <Description>Componente Video Player.</Description> <br/><br/> Il tag video HTML5 consente di incorporare video all’interno di una pagina web senza dover utilizzare plugin esterni. Questo componente utilizza la libreria [video.js](https://videojs.com/) per implementare funzionalità avanzate come il supporto a diversi formati video, la personalizzazione dell’interfaccia utente e l’integrazione con API esterne. <div class=\"callout callout-success\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Accessibilità</span></div> <p>Le persone che utilizzano le tecnologie assistive possono agevolmente accedere ai comandi di questo player video, tuttavia per rendere accessibile un contenuto video è necessario soddisfare i Criteri di Successo contenuti nelle <a href=\"https://www.w3.org/Translations/WCAG21-it/#time-based-media\">linee guida 1.2 Media temporizzati delle WCAG (versione corrente)</a>. In particolare:</p> <ul> <li>Se il contenuto è costituito da “solo video” oppure “solo audio”, è necessario fornire una trascrizione (Criterio di Successo 1.2.1)</li> <li>Fornire sempre sottotitoli (Criterio di Successo 1.2.2).</li> <li>Fornire audio descrizioni quando sono presenti scene o contenuti non descritte dalla traccia audio primaria. (Criteri di Successo 1.2.3 e 1.2.5)</li> </ul></div></div> `, }, }, }, } satisfies Meta<VideoProps>"
330
+ "default": "{ title: 'Componenti/Video', tags: ['a11y-ok', 'web-component'], component: 'it-video', args: { src: 'https://vjs.zencdn.net/v/oceans.webm', poster: '', type: 'video/mp4', options: undefined, track: [], consentOptions: {}, lang: 'it', translations: { it: itLang as any }, initPlugins: '', }, argTypes: { src: { control: 'text', description: 'Sorgente del video' }, poster: { control: 'text', description: \"Sorgente dell'immagine di anteprima\" }, type: { control: 'text', description: 'Tipo del video.', table: { defaultValue: { summary: 'video/mp4' } } }, options: { control: 'object', description: 'Opzioni per il video player. https://videojs.com/guides/options/ qui tutte le opzioni disponibili.', }, track: { control: 'text', table: { defaultValue: { summary: '[{kind:\"chapter\", src: \"/path/file.ext\", srclang:\"it\", label: \"Capitoli\", default: true }]', }, }, description: 'Tracce per didascalie, sottotitoli, capitoli e descrizioni. Nel campo `kind` è necessario indicare la tipologia di traccia fra <ul><li>captions</li><li>subtitles</li><li>description</li><li>chapters</li><li>metadata</li></ul>', }, consentOptions: { name: 'consent-options', control: 'object', description: 'Oggetto per la configurazione del consenso dei cookie. <br/>Di default viene salvata una variabile nel localstorage con lo stesso nome del type del video, ma è possibile personalizzarla passando in `consentOptions` un valore per `consentKey`. <br/>Inoltre, quando viene dato il consenso permanente per i cookie, è possibile personalizzare il comportamento passando in questo oggetto due funzioni specifiche per la gestione della memorizzazione del consenso: `onAccept` e `isAccepted`.', table: { defaultValue: { summary: '{}', }, }, }, lang: { control: 'select', options: ['it', 'en', 'fr', 'de', 'es'], description: \"Lingua del player di cui verrano usate le corrispondenti 'translations'\", table: { defaultValue: { summary: 'it' } }, }, translations: { control: 'object', description: 'Traduzioni del player per le diverse lingue. Di base è disponibile solo la lingua it. Usare questa prop per aggiungere le traduzioni in altre lingue. ', }, initPlugins: { name: 'init-plugins', control: 'text', description: 'Nome della propria funzione presente nella window che verrà invocata da video.js per inizializzare eventuali plugin aggiuntivi definiti dallo sviluppatore.', }, }, decorators: [(Story) => html`<div class=\"sbdocs-video-container\">${Story()}</div>`], } satisfies Meta<VideoProps>"
331
331
  },
332
332
  {
333
333
  "kind": "variable",
@@ -335,15 +335,7 @@
335
335
  "type": {
336
336
  "text": "Story"
337
337
  },
338
- "default": "{ ...meta, name: 'Esempio interattivo', tags: ['!autodocs', '!dev'], parameters: { docs: { source: { excludeDecorators: true }, canvas: { sourceState: 'shown', }, }, }, render: (params) => html` ${renderComponent({ ...params, })}`, }"
339
- },
340
- {
341
- "kind": "variable",
342
- "name": "ComeUsarlo",
343
- "type": {
344
- "text": "Story"
345
- },
346
- "default": "{ name: 'Come usarlo', tags: ['!dev'], render: () => html`<div class=\"hide-preview\"></div>`, parameters: { viewMode: 'docs', // assicura che si apra la tab Docs anziché Canvas docs: { description: { story: ` Per aggiungere un video, è sufficiente utilizzare il componente \\`<it-video />\\` ed i relativi attributi per gestirne la sorgente, e le opzioni del video player. - Usa l'attributo \\`options\\` per passare al player le opzioni definite qui [https://videojs.com/guides/options/](https://videojs.com/guides/options/). - Usa l'attributo \\`translations\\` per definire le traduzioni del player diverse dalla lingua italiana, o per sovrascrivere le traduzioni italiane pre-impostate. ### Font per le icone del player Per utilizzare le icone del player, è necessario includere il font \\`VideoJS.woff\\` nella tua applicazione. Puoi farlo aggiungendo il css compilato di dev-kit-italia nel tuo sorgente HTML: \\`\\`\\`html <link rel=\"stylesheet\" href=\"dev-kit-italia/dist/styles.css\" /> \\`\\`\\` oppure se stai usando SCSS puoi definire il font direttamente nel tuo file SCSS: \\`\\`\\`scss @font-face { font-family: VideoJS; src: url('./assets/fonts/VideoJS.woff') format('woff'); font-weight: normal; font-style: normal; } \\`\\`\\` copiando l'asset \\`VideoJS.woff\\` nella tua cartella assets/fonts (lo puoi copiare dal package dev-kit-italia). ### Plugin Esistono numerosi plugin disponibili per Video.js, che consentono di aggiungere nuove funzionalità, come la riproduzione di video in VR, l’analisi delle statistiche di visualizzazione del video, le utility per la UI mobile e molto altro ancora. #### Utilizzo di ulteriori plugin <Description> (Ad esempio il plugin per l'embed di Vimeo)</Description> Con l'attributo \\`init-plugins\\` è possibile passare al componente \\`<it-video>\\` il nome della propria funzione di inizializzazione dei plugin, che deve essere definita nella window. Esempio: \\`\\`\\`html <it-video ...... init-plugins=\"myInitPlugin\"></it-video> <script> const myInitPlugin = (videojs)=>{ /*my code*/ }</script> \\`\\`\\` `, }, }, }, }"
338
+ "default": "{ ...meta, name: 'Esempio interattivo', tags: ['!dev'], parameters: { docs: { source: { excludeDecorators: true }, canvas: { sourceState: 'closed', }, }, }, render: (params) => html` ${renderComponent({ ...params, })}`, }"
347
339
  },
348
340
  {
349
341
  "kind": "variable",
@@ -359,7 +351,7 @@
359
351
  "type": {
360
352
  "text": "Story"
361
353
  },
362
- "default": "{ ...meta, name: 'Sottotitoli, didascalie, capitoli e descrizioni', parameters: { docs: { description: { story: ` Tramite l'attributo \\`track\\` puoi aggiungere del testo accessibile presente in un file testuale; l’unico formato supportato è WebVTT. L'attributo \\`track\\` accetta un elenco di valori con il seguente formato: \\`\\`\\`js [{kind:'captions', src:'https://example.com/captions.vtt', srclang:'it', label:'Italiano', default:true}, ...] \\`\\`\\` dove ogni singolo elemento rappresenta un file di sottotitoli, didascalie, capitoli o descrizioni. Valorizzando opportunamente la proprietà \\`kind\\` puoi specificare se il file associato contiene: - \\`captions\\`: per i sottotitoli (trascrizione dei dialoghi) - \\`subtitles\\`: per le didascalie (trascrizione dei dialoghi, degli effetti sonori, trascrizione del tipo di emozioni rappresentate dalla musica, ecc) - \\`chapters\\`: per i capitoli - \\`descriptions\\`: per le descrizioni (testo che descrive il contenuto visivo del video, utile per le persone con disabilità visive) - \\`metadata\\`: per i metadati (informazioni aggiuntive sul video, come la durata, la risoluzione, ecc) L'attributo \\`default\\` indica se il file associato è quello predefinito da utilizzare di default quando il video viene caricato. Approfondisci l’argomento consultando la documentazione di [VideoJS](https://videojs.com/guides/text-tracks/) (Inglese). Di seguito un esempio d’uso delle didascalie (kind:\"captions\") in diverse lingue. `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDream.mp4', track: [ { kind: 'captions', src: './assets/video/subtitles-it.vtt', srclang: 'it', label: 'Italiano', default: true }, { kind: 'captions', src: './assets/video/subtitles-en.vtt', srclang: 'en', label: 'English' }, { kind: 'captions', src: './assets/video/subtitles-es.vtt', srclang: 'es', label: 'Español' }, ], translations: undefined, })}`, }"
354
+ "default": "{ ...meta, name: 'Sottotitoli, didascalie, capitoli e descrizioni', render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDream.mp4', track: [ { kind: 'captions', src: './assets/video/subtitles-it.vtt', srclang: 'it', label: 'Italiano', default: true }, { kind: 'captions', src: './assets/video/subtitles-en.vtt', srclang: 'en', label: 'English' }, { kind: 'captions', src: './assets/video/subtitles-es.vtt', srclang: 'es', label: 'Español' }, ], translations: undefined, })}`, }"
363
355
  },
364
356
  {
365
357
  "kind": "variable",
@@ -367,7 +359,7 @@
367
359
  "type": {
368
360
  "text": "Story"
369
361
  },
370
- "default": "{ ...meta, name: 'Immagine di anteprima', parameters: { docs: { description: { story: ` Per aggiungere un’immagine di anteprima come copertina al video occorre utilizzare l’attributo \\`poster\\` inizializzato con la url dell’anteprima. <div class=\"callout callout-warning\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Attenzione</span></div> <p>Le immagini caricate come copertina devono rispettare la stessa \\`aspect ratio\\` del video per una corretta visualizzazione. </p></div></div> `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDream.mp4', poster: './assets/video/ElephantsDream.mp4-poster21.jpg', translations: undefined, })}`, }"
362
+ "default": "{ ...meta, name: 'Immagine di anteprima', parameters: { docs: { description: { story: ` Per aggiungere un’immagine di anteprima come copertina al video, utilizza l’attributo \\`poster\\` inizializzato con la url dell’anteprima. <div class=\"callout callout-warning\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Attenzione</span></div> <p>Le immagini caricate come copertina devono rispettare la stessa \\`aspect ratio\\` del video per una corretta visualizzazione. </p></div></div> `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDream.mp4', poster: './assets/video/ElephantsDream.mp4-poster21.jpg', translations: undefined, })}`, }"
371
363
  },
372
364
  {
373
365
  "kind": "variable",
@@ -375,7 +367,7 @@
375
367
  "type": {
376
368
  "text": "Story"
377
369
  },
378
- "default": "{ ...meta, // name: 'Streaming', parameters: { docs: { description: { story: ` Servire i video tramite dei file in formato mp4 o webm (che sono i formati più supportati) non è la migliore soluzione in termini di performance e di ottimizzazione della banda. Per garantire una buona esperienza utente è fondamentale scegliere il formato di riproduzione più adatto. In questo contesto, i formati di streaming HLS e DASH offrono importanti vantaggi rispetto al tradizionale file MP4. L’uso dei formati di streaming permette una riproduzione fluida dei video online grazie alla loro capacità di adattarsi alla larghezza di banda disponibile. In questo modo si evitano interruzioni o rallentamenti durante la visualizzazione, migliorando l’esperienza utente. Inoltre, questi formati consentono di distribuire il contenuto su diverse piattaforme e dispositivi, aumentando la portabilità del video. <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\"><h5 id=\"tip\">Tip</h5></span></div><p>FFmpeg è uno strumento di conversione multimediale open-source che consente di convertire facilmente i file MP4 in formati adattivi come HLS o DASH, ti permette la conversione del video MP4 in un formato a bitrate variabile per adattare la qualità del video alle diverse velocità di connessione degli utenti. Approfondisci su <a href=\"https://ffmpeg.org/\">FFmpeg</a>.</p></div></div> Le playlist HLS e DASH possono essere riprodotte su più domini condividendo solo l’URL. Tuttavia, a causa delle restrizioni imposte dalle politiche di sicurezza del browser, l’utilizzo di queste playlist in domini diversi da quello originale può causare errori CORS (Cross-Origin Resource Sharing). In altre parole, il browser può rifiutare l’accesso alle risorse audio e video, impedendo la corretta riproduzione del contenuto multimediale. Per superare questo problema, è necessario configurare correttamente il server che fornisce le risorse audio e video, consentendo l’accesso a domini esterni tramite le policy CORS. Di seguito un esempio in formato MPEG-DASH: `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDreamDASH/ElephantsDream.mp4.mpd', type: 'application/dash+xml', poster: './assets/video/ElephantsDream.mp4-poster16.gif', track: [ { kind: 'captions', src: './assets/video/subtitles-it.vtt', srclang: 'it', label: 'Italiano', default: true }, { kind: 'captions', src: './assets/video/subtitles-en.vtt', srclang: 'en', label: 'English' }, { kind: 'captions', src: './assets/video/subtitles-es.vtt', srclang: 'es', label: 'Español' }, { kind: 'chapters', src: './assets/video/chapters-it.vtt', srclang: 'it', label: 'Italiano' }, { kind: 'chapters', src: './assets/video/chapters-en.vtt', srclang: 'en', label: 'English' }, { kind: 'chapters', src: './assets/video/chapters-es.vtt', srclang: 'es', label: 'Español' }, ], translations: undefined, })}`, }"
370
+ "default": "{ ...meta, // name: 'Streaming', parameters: { docs: { description: { story: ` Servire i video tramite dei file in formato mp4 o webm (che sono i formati più supportati) non è la migliore soluzione in termini di performance e di ottimizzazione della banda. Per garantire una buona esperienza utente è fondamentale scegliere il formato di riproduzione più adatto. In questo contesto, i formati di streaming HLS e DASH offrono importanti vantaggi rispetto al tradizionale file MP4. L’uso dei formati di streaming permette una riproduzione fluida dei video online grazie alla loro capacità di adattarsi alla larghezza di banda disponibile. In questo modo si evitano interruzioni o rallentamenti durante la visualizzazione, migliorando l’esperienza utente. Inoltre, questi formati consentono di distribuire il contenuto su diverse piattaforme e dispositivi, aumentando la portabilità del video. <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Suggerimento</span></div><p>FFmpeg è uno strumento di conversione multimediale open-source che consente di convertire facilmente i file MP4 in formati adattivi come HLS o DASH, ti permette la conversione del video MP4 in un formato a bitrate variabile per adattare la qualità del video alle diverse velocità di connessione degli utenti. Approfondisci su <a href=\"https://ffmpeg.org/\">FFmpeg</a>.</p></div></div> Le playlist HLS e DASH possono essere riprodotte su più domini condividendo solo l’URL. Tuttavia, a causa delle restrizioni imposte dalle politiche di sicurezza del browser, l’utilizzo di queste playlist in domini diversi da quello originale può causare errori CORS (Cross-Origin Resource Sharing). In altre parole, il browser può rifiutare l’accesso alle risorse audio e video, impedendo la corretta riproduzione del contenuto multimediale. Per superare questo problema, è necessario configurare correttamente il server che fornisce le risorse audio e video, consentendo l’accesso a domini esterni tramite le policy CORS. Di seguito un esempio in formato MPEG-DASH: `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: './assets/video/ElephantsDreamDASH/ElephantsDream.mp4.mpd', type: 'application/dash+xml', poster: './assets/video/ElephantsDream.mp4-poster16.gif', track: [ { kind: 'captions', src: './assets/video/subtitles-it.vtt', srclang: 'it', label: 'Italiano', default: true }, { kind: 'captions', src: './assets/video/subtitles-en.vtt', srclang: 'en', label: 'English' }, { kind: 'captions', src: './assets/video/subtitles-es.vtt', srclang: 'es', label: 'Español' }, { kind: 'chapters', src: './assets/video/chapters-it.vtt', srclang: 'it', label: 'Italiano' }, { kind: 'chapters', src: './assets/video/chapters-en.vtt', srclang: 'en', label: 'English' }, { kind: 'chapters', src: './assets/video/chapters-es.vtt', srclang: 'es', label: 'Español' }, ], translations: undefined, })}`, }"
379
371
  },
380
372
  {
381
373
  "kind": "variable",
@@ -391,7 +383,7 @@
391
383
  "type": {
392
384
  "text": "Story"
393
385
  },
394
- "default": "{ ...meta, name: 'Embed da piattaforme terze', parameters: { docs: { description: { story: ` Oltre a consentire la riproduzione di video direttamente sulle proprie pagine web, il player video.js offre anche la possibilità di incorporare video provenienti da altre piattaforme come YouTube. Questa funzionalità consente di sfruttare i video già disponibili su queste piattaforme, senza doverli caricare sul proprio sito web. Tuttavia, è importante tenere in considerazione la questione della privacy: quando si incorporano video di terze parti, si può finire per condividere con queste piattaforme i dati degli utenti che visualizzano i video, come ad esempio le informazioni sulla navigazione o l’indirizzo IP. È quindi importante l’utilizzo di questa funzionalità assieme al componente di accettazione del consenso per garantire la protezione della privacy degli utenti. <div class=\"callout callout-warning\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Nota</span></div> <p>Gli esempi che seguono fanno tutti riferimento alla piattaforma di terze parti YouTube.</p> </div></div> <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Responsabilità della privacy</span></div> <p>Coinvolgi il Responsabile per la protezione dei dati (RDP/DPO) della tua amministrazione e ricordati di aggiornare la cookie policy del sito. Designers Italia mette a disposizione il [kit Privacy](https://designers.italia.it/risorse-per-progettare/organizzare/privacy/) per approfondire questi temi e in particolare uno strumento dedicato alla redazione della Cookie policy che trovi in [questa azione del kit](https://designers.italia.it/risorse-per-progettare/organizzare/privacy/rispetta-la-privacy-per-il-go-live-di-un-sito/).</p> </div></div> #### Attivazione dell’overlay di consenso L’utilizzo di un overlay per il consenso è una soluzione comune per garantire la conformità alla normativa sulla privacy in materia di cookie e tracciamento degli utenti. L’overlay per il consenso consente di informare l’utente sui cookie utilizzati e di ottenere il suo consenso in modo esplicito e consapevole alla riproduzione del video prima dell’installazione di qualunque cookie. <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Obblighi</span></div> <p>Per questo la Pubblica Amministrazione che fa uso di servizi di terze parti come YouTube deve necessariamente specificare l’utilizzo di cookie di tracciamento da parte di piattaforme di terze parti, inserendo inoltre il link alla propria cookie policy all’interno dell’overlay (dove adesso c’è il link a ‘#’). Nella sezione seguente vengono illustrate le funzioni per la gestione delle preferenze con JavaScript.</p> </div></div> L'overlay di consenso viene automaticamente istanziato dal componente se si tratta di un video Youtube. Per personalizzare il comportamento sulle scelte effettuate nell'overlay di consenso, è possibile passare al componente \\`<it-video>\\` l'attributo \\`consentOptions\\` con il seguente formato: \\`\\`\\`js consentOptions = { consentKey?: string; //nome della variabile da usare nel localStorage per il salvataggio della preferenza sul consenso. Di default è 'youtube' per i video di Youtube. onAccept?: Function; //(accepted, consentKey)=>{} - funzione che viene invocata quando si accetta il consenso permanente per un video di questa tipologia. Se presente, non viene gestita la preferenza nel localstorage, ma è compito dello sviluppatore implementare la logica di salvataggio delle preferenze isAccepted?: Function; // (consentKey)=>{} - funzione che ritorna un valore booleano (true/false), che indica se l'utente ha gia accettato il consenso permanente per tutti i video di quel tipo. }; \\`\\`\\` Di default sono gia previsti testi e icona, ma è possibile (ed è suggerito) modificare il testo con il link alla pagina della privacy policy. I testi e l'icona sono modificabili attraverso il sistema di traduzioni. Vedi la guida dedicata. `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: 'https://youtu.be/_0j7ZQ67KtY', type: undefined, // translations: undefined, })}`, }"
386
+ "default": "{ ...meta, name: 'Embed da piattaforme terze', parameters: { docs: { description: { story: ` Oltre a consentire la riproduzione di video direttamente sulle proprie pagine web, il player video.js offre anche la possibilità di incorporare video provenienti da altre piattaforme come YouTube. Questa funzionalità consente di sfruttare i video già disponibili su queste piattaforme, senza doverli caricare sul proprio sito web. Tuttavia, è importante tenere in considerazione la questione della privacy: quando si incorporano video di terze parti, si può finire per condividere con queste piattaforme i dati degli utenti che visualizzano i video, come ad esempio le informazioni sulla navigazione o l’indirizzo IP. È quindi importante l’utilizzo di questa funzionalità assieme al componente di accettazione del consenso per garantire la protezione della privacy degli utenti. <div class=\"callout callout-warning\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Nota</span></div> <p>Gli esempi che seguono fanno tutti riferimento alla piattaforma di terze parti YouTube.</p> </div></div> <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Responsabilità della privacy</span></div> <p>Coinvolgi il Responsabile per la protezione dei dati (RDP/DPO) della tua amministrazione e ricordati di aggiornare la cookie policy del sito. Designers Italia mette a disposizione il [kit Privacy](https://designers.italia.it/risorse-per-progettare/organizzare/privacy/) per approfondire questi temi e in particolare uno strumento dedicato alla redazione della Cookie policy che trovi in [questa azione del kit](https://designers.italia.it/risorse-per-progettare/organizzare/privacy/rispetta-la-privacy-per-il-go-live-di-un-sito/).</p> </div></div> ### Attivazione dell’overlay di consenso L’utilizzo di un overlay per il consenso è una soluzione comune per garantire la conformità alla normativa sulla privacy in materia di cookie e tracciamento degli utenti. L’overlay per il consenso consente di informare l’utente sui cookie utilizzati e di ottenere il suo consenso in modo esplicito e consapevole alla riproduzione del video prima dell’installazione di qualunque cookie. <div class=\"callout callout-info\"><div class=\"callout-inner\"><div class=\"callout-title\"><span class=\"text\">Obblighi</span></div> <p>Per questo la Pubblica Amministrazione che fa uso di servizi di terze parti come YouTube deve necessariamente specificare l’utilizzo di cookie di tracciamento da parte di piattaforme di terze parti, inserendo inoltre il link alla propria cookie policy all’interno dell’overlay (dove adesso c’è il link a ‘#’). Nella sezione seguente vengono illustrate le funzioni per la gestione delle preferenze con JavaScript.</p> </div></div> L'overlay di consenso viene automaticamente istanziato dal componente se si tratta di un video Youtube. Per personalizzare il comportamento sulle scelte effettuate nell'overlay di consenso, è possibile passare al componente \\`<it-video>\\` l'attributo \\`consentOptions\\` con il seguente formato: \\`\\`\\`js consentOptions = { consentKey?: string; //nome della variabile da usare nel localStorage per il salvataggio della preferenza sul consenso. Di default è 'youtube' per i video di Youtube. onAccept?: Function; //(accepted, consentKey)=>{} - funzione che viene invocata quando si accetta il consenso permanente per un video di questa tipologia. Se presente, non viene gestita la preferenza nel localstorage, ma è compito dello sviluppatore implementare la logica di salvataggio delle preferenze isAccepted?: Function; // (consentKey)=>{} - funzione che ritorna un valore booleano (true/false), che indica se l'utente ha gia accettato il consenso permanente per tutti i video di quel tipo. }; \\`\\`\\` Di default sono gia previsti testi e icona, ma è possibile (ed è suggerito) modificare il testo con il link alla pagina della privacy policy. I testi e l'icona sono modificabili attraverso il sistema di traduzioni. Vedi la guida dedicata. `, }, }, }, render: (params) => html`${renderComponent({ ...params, src: 'https://youtu.be/_0j7ZQ67KtY', type: undefined, // translations: undefined, })}`, }"
395
387
  },
396
388
  {
397
389
  "kind": "variable",
@@ -419,14 +411,6 @@
419
411
  "module": "stories/it-video.stories.ts"
420
412
  }
421
413
  },
422
- {
423
- "kind": "js",
424
- "name": "ComeUsarlo",
425
- "declaration": {
426
- "name": "ComeUsarlo",
427
- "module": "stories/it-video.stories.ts"
428
- }
429
- },
430
414
  {
431
415
  "kind": "js",
432
416
  "name": "ConTrascrizione",
@@ -1 +1 @@
1
- {"version":3,"file":"it-video.d.ts","sourceRoot":"","sources":["../../src/it-video.ts"],"names":[],"mappings":"AAKA,OAAO,EAAW,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAKlE,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQvF,qBACa,OAAQ,SAAQ,sBAAsB;IACjD,MAAM,CAAC,MAAM,iCAAY;IAEzB,OAAO,CAAC,OAAO,CAAoD;IAEvC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,IAAI,EAAE,MAAM,CAAe;IAE3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAGnE,QAAQ,SAAQ;IAE2B,YAAY,EAAE,mBAAmB,CAAkB;IAEnE,KAAK,EAAE,KAAK,CAAM;IAEjB,cAAc,CAAC,EAAE,cAAc,CAAM;IAGjE,eAAe,SAAM;IAGrB,OAAO,CAAC,MAAM,CAAa;IAG3B,OAAO,CAAC,YAAY,CAAa;IAGjC,OAAO,CAAC,eAAe,CAAkB;IAEzC;;OAEG;IACH,OAAO,KAAK,aAAa,GAGxB;IAKD,YAAY;IAUZ,kBAAkB;IAUlB,aAAa;IAQb,aAAa,CAAC,QAAQ,GAAE,OAAe;IAoBvC,iBAAiB;IAKjB,eAAe;IAmBf,eAAe,CAAC,eAAe,GAAE,OAAe;IAkFhD,YAAY;IAKZ,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;IAQ9C,MAAM;IAgCN,iBAAiB,IAAI,IAAI;IAWzB,oBAAoB,IAAI,IAAI;CAO7B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,wBAAwB,CAAC,EAAE,OAAO,CAAC;KACpC;IACD,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
1
+ {"version":3,"file":"it-video.d.ts","sourceRoot":"","sources":["../../src/it-video.ts"],"names":[],"mappings":"AAKA,OAAO,EAAW,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAKlE,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQvF,qBACa,OAAQ,SAAQ,sBAAsB;IACjD,MAAM,CAAC,MAAM,iCAAY;IAEzB,OAAO,CAAC,OAAO,CAAoD;IAEvC,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,IAAI,EAAE,MAAM,CAAe;IAE3B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAGnE,QAAQ,SAAQ;IAE2B,YAAY,EAAE,mBAAmB,CAAkB;IAEnE,KAAK,EAAE,KAAK,CAAM;IAEjB,cAAc,CAAC,EAAE,cAAc,CAAM;IAGjE,eAAe,SAAM;IAGrB,OAAO,CAAC,MAAM,CAAa;IAG3B,OAAO,CAAC,YAAY,CAAa;IAGjC,OAAO,CAAC,eAAe,CAAkB;IAEzC;;OAEG;IACH,OAAO,KAAK,aAAa,GAGxB;IAKD,YAAY;IAUZ,kBAAkB;IAUlB,aAAa;IAQb,aAAa,CAAC,QAAQ,GAAE,OAAe;IAoBvC,iBAAiB;IAKjB,eAAe;IAmBf,eAAe,CAAC,eAAe,GAAE,OAAe;IAmFhD,YAAY;IAKZ,UAAU,CAAC,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC;IAQ9C,MAAM;IAoCN,iBAAiB,IAAI,IAAI;IAWzB,oBAAoB,IAAI,IAAI;CAO7B;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,wBAAwB,CAAC,EAAE,OAAO,CAAC;KACpC;IACD,UAAU,qBAAqB;QAC7B,UAAU,EAAE,OAAO,CAAC;KACrB;CACF"}
@@ -70397,6 +70397,7 @@ const interactions = new WeakMap();
70397
70397
  /** A reactive controller to allow form controls to participate in form submission, validation, etc. */
70398
70398
  class FormControlController {
70399
70399
  constructor(host, options) {
70400
+ this.submittedOnce = false;
70400
70401
  this.handleFormData = (event) => {
70401
70402
  // console.log('handleFormData');
70402
70403
  const disabled = this.options.disabled(this.host);
@@ -70418,6 +70419,17 @@ class FormControlController {
70418
70419
  event.formData.append(name, value);
70419
70420
  }
70420
70421
  break;
70422
+ case 'it-checkbox':
70423
+ if (this.host.checked) {
70424
+ if (event.formData.getAll(name).indexOf(value) < 0) {
70425
+ // handle group checkbox
70426
+ event.formData.append(name, value);
70427
+ }
70428
+ }
70429
+ break;
70430
+ case 'it-checkbox-group':
70431
+ // non settare valori in formData, perchè ogni singola checkbox setta il suo valore
70432
+ break;
70421
70433
  default:
70422
70434
  if (Array.isArray(value)) {
70423
70435
  value.forEach((val) => {
@@ -70439,13 +70451,30 @@ class FormControlController {
70439
70451
  this.setUserInteracted(control, true);
70440
70452
  });
70441
70453
  }
70442
- if (this.form && !this.form.noValidate && !disabled && !reportValidity(this.host)) {
70454
+ const resReportValidity = reportValidity(this.host);
70455
+ if (this.form && !this.form.noValidate && !disabled && !resReportValidity) {
70443
70456
  event.preventDefault();
70444
70457
  // event.stopImmediatePropagation(); // se lo attiviamo, valida un campo alla volta
70458
+ // Scroll al primo controllo non valido
70459
+ const formControls = formCollections.get(this.form);
70460
+ if (formControls) {
70461
+ for (const control of formControls) {
70462
+ if (!control.validity?.valid) {
70463
+ // Scroll smooth verso il controllo non valido
70464
+ control.scrollIntoView({
70465
+ behavior: 'smooth',
70466
+ block: 'center',
70467
+ });
70468
+ break;
70469
+ }
70470
+ }
70471
+ }
70445
70472
  }
70473
+ this.submittedOnce = true;
70446
70474
  };
70447
70475
  this.handleFormReset = () => {
70448
70476
  this.options.setValue(this.host, '');
70477
+ this.submittedOnce = false;
70449
70478
  this.setUserInteracted(this.host, false);
70450
70479
  interactions.set(this.host, []);
70451
70480
  };
@@ -70610,6 +70639,7 @@ class FormControlController {
70610
70639
  if (!formCollection) {
70611
70640
  return;
70612
70641
  }
70642
+ this.submittedOnce = false;
70613
70643
  // Remove this host from the form's collection
70614
70644
  formCollection.delete(this.host);
70615
70645
  // Check to make sure there's no other form controls in the collection. If we do this
@@ -70706,6 +70736,10 @@ class FormControlController {
70706
70736
  host.toggleAttribute('data-user-invalid', !isValid && hasInteracted);
70707
70737
  host.toggleAttribute('data-user-valid', isValid && hasInteracted);
70708
70738
  }
70739
+ userInteracted() {
70740
+ const hasInteracted = Boolean(userInteractedControls.has(this.host));
70741
+ return hasInteracted;
70742
+ }
70709
70743
  /**
70710
70744
  * Updates the form control's validity based on the current value of `host.validity.valid`. Call this when anything
70711
70745
  * that affects constraint validation changes so the component receives the correct validity states.
@@ -70742,6 +70776,7 @@ const translation$2 = {
70742
70776
  $name: 'Italiano',
70743
70777
  $dir: 'ltr',
70744
70778
  validityRequired: 'Questo campo è obbligatorio.',
70779
+ validityGroupRequired: "Scegli almeno un'opzione",
70745
70780
  validityPattern: 'Il valore non corrisponde al formato richiesto.',
70746
70781
  validityMinlength: 'Il valore deve essere lungo almeno {minlength} caratteri.',
70747
70782
  validityMaxlength: 'Il valore deve essere lungo al massimo {maxlength} caratteri.',
@@ -70786,24 +70821,27 @@ class FormControl extends BaseLocalizedComponent {
70786
70821
  this.maxlength = -1;
70787
70822
  /** If the input is required. */
70788
70823
  this.required = false;
70824
+ /* For grouped input, like checkbox-group */
70825
+ this.isInGroup = false;
70789
70826
  this.validationMessage = '';
70790
70827
  }
70791
70828
  /** Gets the validity state object */
70792
70829
  get validity() {
70793
70830
  return this.inputElement?.validity;
70794
70831
  }
70832
+ /** Gets the associated form, if one exists. */
70833
+ getForm() {
70834
+ return this.formControlController.getForm();
70835
+ }
70795
70836
  // Form validation methods
70796
70837
  checkValidity() {
70797
70838
  const inputValid = this.inputElement?.checkValidity() ?? true; // this.inputElement.checkValidity() è la validazione del browser
70798
70839
  return inputValid;
70799
70840
  }
70800
- /** Gets the associated form, if one exists. */
70801
- getForm() {
70802
- return this.formControlController.getForm();
70803
- }
70804
70841
  /** Checks for validity and shows the browser's validation message if the control is invalid. */
70805
70842
  reportValidity() {
70806
- const ret = this.inputElement.checkValidity();
70843
+ // const ret = this.inputElement.checkValidity();
70844
+ const ret = this.checkValidity(); // chiama la checkValidity, che se è stata overridata chiama quella
70807
70845
  this.handleValidationMessages();
70808
70846
  return ret; // this.inputElement.reportValidity();
70809
70847
  }
@@ -70848,7 +70886,8 @@ class FormControl extends BaseLocalizedComponent {
70848
70886
  handleValidationMessages() {
70849
70887
  if (!this.customValidation) {
70850
70888
  const _v = this.inputElement.validity;
70851
- if (_v.valueMissing) {
70889
+ const isRequiredHandledByGroup = this.isInGroup === true;
70890
+ if (_v.valueMissing && !isRequiredHandledByGroup) {
70852
70891
  this.setCustomValidity(this.$t('validityRequired'));
70853
70892
  }
70854
70893
  else if (_v.patternMismatch) {
@@ -70919,7 +70958,7 @@ class FormControl extends BaseLocalizedComponent {
70919
70958
  if (this.customValidation) {
70920
70959
  this.setCustomValidity(this.validationText ?? '');
70921
70960
  }
70922
- else {
70961
+ else if (this.formControlController.userInteracted()) {
70923
70962
  this.formControlController.updateValidity();
70924
70963
  }
70925
70964
  }
@@ -70987,6 +71026,10 @@ __decorate([
70987
71026
  ,
70988
71027
  __metadata("design:type", Object)
70989
71028
  ], FormControl.prototype, "required", void 0);
71029
+ __decorate([
71030
+ property({ type: Boolean }),
71031
+ __metadata("design:type", Object)
71032
+ ], FormControl.prototype, "isInGroup", void 0);
70990
71033
  __decorate([
70991
71034
  state(),
70992
71035
  __metadata("design:type", Object)
@@ -71022,6 +71065,14 @@ const cookies = {
71022
71065
  clearAllRememberedChoices,
71023
71066
  };
71024
71067
 
71068
+ if (typeof window !== 'undefined') {
71069
+ window._itAnalytics = window._itAnalytics || {};
71070
+ window._itAnalytics = {
71071
+ ...window._itAnalytics,
71072
+ version: '1.0.0-alpha.4',
71073
+ };
71074
+ }
71075
+
71025
71076
  /* eslint-disable class-methods-use-this */
71026
71077
  /* eslint-disable prefer-destructuring */
71027
71078
  /* eslint-disable dot-notation */
@@ -74132,8 +74183,14 @@ video::-webkit-media-text-track-display {
74132
74183
  min-height: 450px;
74133
74184
  }
74134
74185
 
74186
+ .acceptoverlayable {
74187
+ aspect-ratio: 16 / 9;
74188
+ }
74189
+
74135
74190
  .acceptoverlay {
74136
74191
  --bs-acceptoverlay-color-text: #fff;
74192
+ --bs-form-control-label-color: #fff;
74193
+ --bs-form-checkbox-border-color: #fff;
74137
74194
  }
74138
74195
  .acceptoverlay a {
74139
74196
  color: var(--bs-acceptoverlay-color-text);
@@ -74184,9 +74241,15 @@ video:not([width]) {
74184
74241
  width: var(--it-videojs-default-width);
74185
74242
  height: var(--it-videojs-default-height);
74186
74243
  }
74187
-
74188
- .vjs-fluid:not(.vjs-audio-only-mode) {
74189
- padding-top: 56.25%;
74244
+ .video-js .vjs-tech {
74245
+ position: inherit !important;
74246
+ height: auto !important;
74247
+ }
74248
+ .video-js.vjs-youtube .vjs-tech {
74249
+ aspect-ratio: 16 / 9;
74250
+ }
74251
+ .video-js:not(.vjs-audio-only-mode).vjs-fluid, .video-js:not(.vjs-audio-only-mode).vjs-16-9, .video-js:not(.vjs-audio-only-mode).vjs-4-3, .video-js:not(.vjs-audio-only-mode).vjs-9-16, .video-js:not(.vjs-audio-only-mode).vjs-1-1, .video-js:not(.vjs-audio-only-mode).vjs-youtube {
74252
+ height: auto;
74190
74253
  }`;
74191
74254
 
74192
74255
  registerTranslation(translation);
@@ -74320,26 +74383,28 @@ let ItVideo = class ItVideo extends BaseLocalizedComponent {
74320
74383
  initYoutubePlugin(videojsFn);
74321
74384
  }
74322
74385
  this.initPluginsFn?.(videojsFn); // se passata una funzione di init di ulteriori plugin, la chiama.
74323
- this.player = videojsFn(this.videoElement, mergedOptions, function onPlayerReady() {
74324
- this.addClass('vjs-theme-bootstrap-italia');
74325
- this.addClass('vjs-big-play-centered');
74326
- // Aggiungi i track manualmente
74327
- tracks.forEach((t) => {
74328
- this.addRemoteTextTrack({
74329
- kind: t.kind,
74330
- src: t.src,
74331
- srclang: t.srclang || this.language,
74332
- label: t.label,
74333
- default: !!t.default,
74334
- }, false);
74335
- });
74336
- if (focusPlayButton) {
74337
- const playButton = this.el()?.querySelector('.vjs-big-play-button');
74338
- if (playButton) {
74339
- playButton.focus();
74386
+ if (this.videoElement) {
74387
+ this.player = videojsFn(this.videoElement, mergedOptions, function onPlayerReady() {
74388
+ this.addClass('vjs-theme-bootstrap-italia');
74389
+ this.addClass('vjs-big-play-centered');
74390
+ // Aggiungi i track manualmente
74391
+ tracks.forEach((t) => {
74392
+ this.addRemoteTextTrack({
74393
+ kind: t.kind,
74394
+ src: t.src,
74395
+ srclang: t.srclang || this.language,
74396
+ label: t.label,
74397
+ default: !!t.default,
74398
+ }, false);
74399
+ });
74400
+ if (focusPlayButton) {
74401
+ const playButton = this.el()?.querySelector('.vjs-big-play-button');
74402
+ if (playButton) {
74403
+ playButton.focus();
74404
+ }
74340
74405
  }
74341
- }
74342
- });
74406
+ });
74407
+ }
74343
74408
  }
74344
74409
  }
74345
74410
  /*
@@ -74375,10 +74440,14 @@ let ItVideo = class ItVideo extends BaseLocalizedComponent {
74375
74440
  ${this.$t('video_consent_accept')}
74376
74441
  </it-button>
74377
74442
 
74378
- <div class="form-check">
74379
- <input id="chk-remember" type="checkbox" @click=${() => this.acceptConsent(true)} />
74380
- <label for="chk-remember">${this.$t('video_consent_remember')}</label>
74381
- </div>
74443
+ <it-checkbox
74444
+ @click=${(e) => {
74445
+ e.preventDefault();
74446
+ e.stopPropagation();
74447
+ this.acceptConsent(true);
74448
+ }}
74449
+ ><span slot="label">${this.$t('video_consent_remember')}</span></it-checkbox
74450
+ >
74382
74451
  </div>
74383
74452
  </div>
74384
74453
  </div>