@nysds/nys-video 1.16.1 → 1.17.0

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/dist/nys-video.js CHANGED
@@ -5,7 +5,7 @@ import { property as n, state as c } from "lit/decorators.js";
5
5
  * █ █ █ █▄▄▄█ ▀▀▀▄▄ █ █ ▀▀▀▄▄
6
6
  * █ ▀█ █ █▄▄▄█ █▄▄▀ █▄▄▄█
7
7
  *
8
- * Video Component v1.16.1
8
+ * Video Component v1.17.0
9
9
  * Part of the New York State Design System
10
10
  * Repository: https://github.com/its-hcd/nysds
11
11
  * License: MIT
@@ -18,7 +18,10 @@ var _ = Object.defineProperty, o = (y, e, t, r) => {
18
18
  };
19
19
  let b = 0;
20
20
  const l = class l extends u {
21
- // Lifecycle Methods
21
+ /**
22
+ * Lifecycle methods
23
+ * --------------------------------------------------------------------------
24
+ */
22
25
  constructor() {
23
26
  super(), this.id = "", this.titleText = "", this.videourl = "", this.size = "", this.loading = "lazy", this.starttime = 0, this.thumbnail = null, this.autoplay = !1, this.disabled = !1, this._playerActive = !1, this._announcement = "", this._adPlaying = !1;
24
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"nys-video.js","sources":["../src/nys-video.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-video.scss?inline\";\n\ndeclare global {\n interface Window {\n YT: any;\n onYouTubeIframeAPIReady: () => void;\n }\n}\n\nlet videoIdCounter = 0;\n\nexport class NysVideo extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Full YouTube URL — required. Component will not render if invalid. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Title text for the thumbnail of the video */\n @property({ type: String, reflect: true }) titleText = \"\";\n\n /** Full YouTube URL — required. Component will not render if invalid. */\n @property({ type: String }) videourl = \"\";\n\n /**\n * Largest size for the video player.\n * If not set, size is determined automatically by viewport width.\n */\n @property({ type: String, reflect: true }) size: \"full\" | \"md\" | \"sm\" | \"\" =\n \"\";\n\n @property({ type: String }) loading: \"lazy\" | \"eager\" = \"lazy\";\n\n // /** Accessible label describing the video content **/\n // @property({ type: String }) ariaLabel = \"\";\n\n /** Time in seconds where playback begins. **/\n @property({ type: Number }) starttime = 0;\n\n /**\n * Custom thumbnail image path.\n * Falls back to YouTube's auto-generated thumbnail if not provided.\n */\n @property({ type: String }) thumbnail: string | null = null;\n\n /** Triggers autoplay when the iframe loads */\n @property({ type: Boolean }) autoplay = false;\n\n /** Prevents the video from being played */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Tracks whether the user has clicked to load the player */\n @state() private _playerActive = false;\n\n /** Screen reader announcement text */\n @state() private _announcement = \"\";\n\n /** Tracks whether an ad is currently playing to suppress false \"Video is playing\" announcements */\n private _adPlaying = false;\n\n // Lifecycle Methods\n constructor() {\n super();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-video-${Date.now()}-${videoIdCounter++}`;\n }\n\n if (this.autoplay) {\n this._announceVideoVO();\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n private _isValidYouTubeUrl(): boolean {\n return /^(https?:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.be)\\/.+/.test(\n this.videourl,\n );\n }\n\n private _getVideoId() {\n const regExp =\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|shorts\\/)|youtu\\.be\\/)([^?&]+)/;\n const match = this.videourl.match(regExp);\n return match ? match[1] : null;\n }\n\n private _getThumbnailUrl() {\n if (this.thumbnail) return this.thumbnail;\n const videoId = this._getVideoId();\n return `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;\n }\n\n private _getEmbedUrl() {\n const videoId = this._getVideoId();\n if (!videoId) return null;\n\n const params = new URLSearchParams({\n autoplay: \"1\",\n enablejsapi: \"1\",\n ...(this.autoplay && { mute: \"1\" }),\n ...(this.starttime > 0 && { start: String(this.starttime) }),\n });\n\n return `https://www.youtube.com/embed/${videoId}?${params}`;\n }\n\n /**\n * Because I need to know if Youtube ADs are playing, I need to call YT's API.\n * Hence, the YT API setup below. The VO has 2 types of announcements:\n * - \"Advertisement is playing\"\n * - \"Video is playing\"\n *\n * YT IFrame Player API: https://developers.google.com/youtube/iframe_api_reference\n */\n private _announceVideoVO() {\n const setup = () => {\n this.updateComplete.then(() => {\n const iframe = this.shadowRoot?.querySelector(\"iframe\");\n if (!iframe) return;\n\n new window.YT.Player(iframe, {\n events: {\n onStateChange: (event: { data: number }) => {\n if (this._adPlaying) return;\n\n if (event.data === window.YT.PlayerState.PLAYING) {\n this._announcement = this.autoplay\n ? \"Video is playing, muted\"\n : \"Video is playing\";\n setTimeout(() => (this._announcement = \"\"), 1000);\n }\n },\n // // NOTE: onAdStateChange is not officially documented by YouTube.\n // It is a real event fired by the IFrame player, discovered through community reverse-engineering\n onAdStateChange: (event: { data: number }) => {\n this._adPlaying = event.data === window.YT.PlayerState.PLAYING;\n\n if (this._adPlaying) {\n this._announcement = \"Advertisement is playing\";\n setTimeout(() => (this._announcement = \"\"), 1000);\n }\n },\n },\n });\n });\n };\n\n // The window.YT is YouTube's API object. If already loaded, run setup immediately.\n if (window.YT?.Player) {\n setup();\n } else {\n if (!document.getElementById(\"yt-iframe-api\")) {\n const script = document.createElement(\"script\");\n script.id = \"yt-iframe-api\";\n script.src = \"https://www.youtube.com/iframe_api\";\n document.head.appendChild(script);\n }\n window.onYouTubeIframeAPIReady = setup;\n }\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n private _handleThumbnailClick() {\n if (this.disabled) return;\n this._playerActive = true;\n\n this.updateComplete.then(() => {\n const iframe = this.shadowRoot?.querySelector(\"iframe\");\n if (!iframe) return;\n\n iframe.addEventListener(\n \"load\",\n () => {\n iframe.focus();\n },\n { once: true },\n );\n });\n\n this._announceVideoVO();\n }\n\n /**\n * Render Helpers\n * --------------------------------------------------------------------------\n */\n private _renderAnnouncer() {\n return html`\n <div\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n class=\"nys-video__announcer sr-only\"\n >\n ${this._announcement}\n </div>\n `;\n }\n\n private _renderPlayIcon() {\n return this.disabled\n ? html`<svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"31\"\n height=\"35\"\n viewBox=\"0 0 31 35\"\n fill=\"none\"\n >\n <path\n d=\"M29.4221 15.7357L2.568 0.231711C1.42656 -0.426849 0 0.396831 0 1.71395V32.7229C0 34.041 1.42656 34.8647 2.568 34.2052L29.4221 18.7012C30.5635 18.0426 30.5635 16.3952 29.4221 15.7357Z\"\n fill=\"white\"\n fill-opacity=\"0.4\"\n />\n </svg>`\n : html`<svg\n aria-hidden=\"true\"\n width=\"31\"\n height=\"35\"\n viewBox=\"0 0 31 35\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M29.4221 15.7357L2.568 0.231711C1.42656 -0.426849 0 0.396831 0 1.71395V32.7229C0 34.041 1.42656 34.8647 2.568 34.2052L29.4221 18.7012C30.5635 18.0426 30.5635 16.3952 29.4221 15.7357Z\"\n fill=\"white\"\n />\n </svg>`;\n }\n\n render() {\n if (!this._isValidYouTubeUrl()) {\n return html``;\n }\n\n const embedUrl = this._getEmbedUrl();\n if (!embedUrl) return html``;\n\n // Use explicit size if set, otherwise fall back to auto-computed size\n const effectiveSize = this.size || \"md\";\n\n /**\n * Show thumbnail first, unless until user clicks or developer sets \"autoplay\".\n * Then, swap to iframe.\n */\n if (!this._playerActive && !this.autoplay) {\n return html`\n <div\n class=\"nys-video nys-video--${effectiveSize} ${this.disabled\n ? \"nys-video--disabled\"\n : \"\"}\"\n >\n ${this._renderAnnouncer()}\n <div class=\"nys-video__ratio-box\">\n <div\n class=\"nys-video__thumbnail\"\n @click=${this._handleThumbnailClick}\n >\n <img src=${this._getThumbnailUrl()} alt=\"\" />\n <button\n class=\"nys-video__play-icon\"\n aria-label=\"Play ${this.titleText}\"\n ?disabled=${this.disabled}\n >\n ${this._renderPlayIcon()}\n </button>\n </div>\n </div>\n ${this.titleText &&\n html`<div class=\"nys-video__title-text\">\n <p>${this.titleText}</p>\n </div>`}\n </div>\n `;\n }\n\n return html`<div class=\"nys-video nys-video--${effectiveSize}\">\n ${this._renderAnnouncer()}\n <div class=\"nys-video__ratio-box\">\n <iframe\n tabindex=\"0\"\n src=${embedUrl}\n title=${this.titleText}\n aria-label=${this.titleText}\n loading=${this.loading}\n allowfullscreen\n frameborder=\"0\"\n allow=\"accelerometer;autoplay;clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n ></iframe>\n </div>\n </div>`;\n }\n}\n\nif (!customElements.get(\"nys-video\")) {\n customElements.define(\"nys-video\", NysVideo);\n}\n"],"names":["videoIdCounter","_NysVideo","LitElement","regExp","match","videoId","params","setup","iframe","event","script","html","embedUrl","effectiveSize","unsafeCSS","styles","NysVideo","__decorateClass","property","state"],"mappings":"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"}
1
+ {"version":3,"file":"nys-video.js","sources":["../src/nys-video.ts"],"sourcesContent":["import { LitElement, html, unsafeCSS } from \"lit\";\nimport { property, state } from \"lit/decorators.js\";\n// @ts-ignore: SCSS module imported via bundler as inline\nimport styles from \"./nys-video.scss?inline\";\n\ndeclare global {\n interface Window {\n YT: any;\n onYouTubeIframeAPIReady: () => void;\n }\n}\n\nlet videoIdCounter = 0;\n\n/**\n * A YouTube video player with a thumbnail preview and play button.\n * Loads the iframe only after the user clicks play, keeping initial page load light.\n * Supports autoplay (muted), custom thumbnails, start time, lazy loading, and disabled state.\n * Announces playback state and ad state to screen readers via a live region.\n *\n * For use with YouTube URLs only. Component renders nothing if the URL is invalid.\n *\n * @summary YouTube video player with thumbnail preview and accessibility announcements.\n * @element nys-video\n *\n * @fires nys-video-play - Fired when the user clicks the thumbnail to load the player.\n *\n * @example Basic usage\n * ```html\n * <nys-video\n * videourl=\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"\n * titleText=\"Video Title\"\n * ></nys-video>\n * ```\n */\n\nexport class NysVideo extends LitElement {\n static styles = unsafeCSS(styles);\n\n /** Full YouTube URL — required. Component will not render if invalid. */\n @property({ type: String, reflect: true }) id = \"\";\n\n /** Title text for the thumbnail of the video */\n @property({ type: String, reflect: true }) titleText = \"\";\n\n /** Full YouTube URL — required. Component will not render if invalid. */\n @property({ type: String }) videourl = \"\";\n\n /**\n * Largest size for the video player.\n * If not set, size is determined automatically by viewport width.\n */\n @property({ type: String, reflect: true }) size: \"full\" | \"md\" | \"sm\" | \"\" =\n \"\";\n\n @property({ type: String }) loading: \"lazy\" | \"eager\" = \"lazy\";\n\n // /** Accessible label describing the video content **/\n // @property({ type: String }) ariaLabel = \"\";\n\n /** Time in seconds where playback begins. **/\n @property({ type: Number }) starttime = 0;\n\n /**\n * Custom thumbnail image path.\n * Falls back to YouTube's auto-generated thumbnail if not provided.\n */\n @property({ type: String }) thumbnail: string | null = null;\n\n /** Triggers autoplay when the iframe loads */\n @property({ type: Boolean }) autoplay = false;\n\n /** Prevents the video from being played */\n @property({ type: Boolean, reflect: true }) disabled = false;\n\n /** Tracks whether the user has clicked to load the player */\n @state() private _playerActive = false;\n\n /** Screen reader announcement text */\n @state() private _announcement = \"\";\n\n /** Tracks whether an ad is currently playing to suppress false \"Video is playing\" announcements */\n private _adPlaying = false;\n\n /**\n * Lifecycle methods\n * --------------------------------------------------------------------------\n */\n\n constructor() {\n super();\n }\n\n // Generate a unique ID if one is not provided\n connectedCallback() {\n super.connectedCallback();\n if (!this.id) {\n this.id = `nys-video-${Date.now()}-${videoIdCounter++}`;\n }\n\n if (this.autoplay) {\n this._announceVideoVO();\n }\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n }\n\n /**\n * Functions\n * --------------------------------------------------------------------------\n */\n private _isValidYouTubeUrl(): boolean {\n return /^(https?:\\/\\/)?(www\\.)?(youtube\\.com|youtu\\.be)\\/.+/.test(\n this.videourl,\n );\n }\n\n private _getVideoId() {\n const regExp =\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/|shorts\\/)|youtu\\.be\\/)([^?&]+)/;\n const match = this.videourl.match(regExp);\n return match ? match[1] : null;\n }\n\n private _getThumbnailUrl() {\n if (this.thumbnail) return this.thumbnail;\n const videoId = this._getVideoId();\n return `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`;\n }\n\n private _getEmbedUrl() {\n const videoId = this._getVideoId();\n if (!videoId) return null;\n\n const params = new URLSearchParams({\n autoplay: \"1\",\n enablejsapi: \"1\",\n ...(this.autoplay && { mute: \"1\" }),\n ...(this.starttime > 0 && { start: String(this.starttime) }),\n });\n\n return `https://www.youtube.com/embed/${videoId}?${params}`;\n }\n\n /**\n * Because I need to know if Youtube ADs are playing, I need to call YT's API.\n * Hence, the YT API setup below. The VO has 2 types of announcements:\n * - \"Advertisement is playing\"\n * - \"Video is playing\"\n *\n * YT IFrame Player API: https://developers.google.com/youtube/iframe_api_reference\n */\n private _announceVideoVO() {\n const setup = () => {\n this.updateComplete.then(() => {\n const iframe = this.shadowRoot?.querySelector(\"iframe\");\n if (!iframe) return;\n\n new window.YT.Player(iframe, {\n events: {\n onStateChange: (event: { data: number }) => {\n if (this._adPlaying) return;\n\n if (event.data === window.YT.PlayerState.PLAYING) {\n this._announcement = this.autoplay\n ? \"Video is playing, muted\"\n : \"Video is playing\";\n setTimeout(() => (this._announcement = \"\"), 1000);\n }\n },\n // // NOTE: onAdStateChange is not officially documented by YouTube.\n // It is a real event fired by the IFrame player, discovered through community reverse-engineering\n onAdStateChange: (event: { data: number }) => {\n this._adPlaying = event.data === window.YT.PlayerState.PLAYING;\n\n if (this._adPlaying) {\n this._announcement = \"Advertisement is playing\";\n setTimeout(() => (this._announcement = \"\"), 1000);\n }\n },\n },\n });\n });\n };\n\n // The window.YT is YouTube's API object. If already loaded, run setup immediately.\n if (window.YT?.Player) {\n setup();\n } else {\n if (!document.getElementById(\"yt-iframe-api\")) {\n const script = document.createElement(\"script\");\n script.id = \"yt-iframe-api\";\n script.src = \"https://www.youtube.com/iframe_api\";\n document.head.appendChild(script);\n }\n window.onYouTubeIframeAPIReady = setup;\n }\n }\n\n /**\n * Event Handlers\n * --------------------------------------------------------------------------\n */\n\n private _handleThumbnailClick() {\n if (this.disabled) return;\n this._playerActive = true;\n\n this.updateComplete.then(() => {\n const iframe = this.shadowRoot?.querySelector(\"iframe\");\n if (!iframe) return;\n\n iframe.addEventListener(\n \"load\",\n () => {\n iframe.focus();\n },\n { once: true },\n );\n });\n\n this._announceVideoVO();\n }\n\n /**\n * Render Helpers\n * --------------------------------------------------------------------------\n */\n private _renderAnnouncer() {\n return html`\n <div\n aria-live=\"assertive\"\n aria-atomic=\"true\"\n class=\"nys-video__announcer sr-only\"\n >\n ${this._announcement}\n </div>\n `;\n }\n\n private _renderPlayIcon() {\n return this.disabled\n ? html`<svg\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"31\"\n height=\"35\"\n viewBox=\"0 0 31 35\"\n fill=\"none\"\n >\n <path\n d=\"M29.4221 15.7357L2.568 0.231711C1.42656 -0.426849 0 0.396831 0 1.71395V32.7229C0 34.041 1.42656 34.8647 2.568 34.2052L29.4221 18.7012C30.5635 18.0426 30.5635 16.3952 29.4221 15.7357Z\"\n fill=\"white\"\n fill-opacity=\"0.4\"\n />\n </svg>`\n : html`<svg\n aria-hidden=\"true\"\n width=\"31\"\n height=\"35\"\n viewBox=\"0 0 31 35\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n d=\"M29.4221 15.7357L2.568 0.231711C1.42656 -0.426849 0 0.396831 0 1.71395V32.7229C0 34.041 1.42656 34.8647 2.568 34.2052L29.4221 18.7012C30.5635 18.0426 30.5635 16.3952 29.4221 15.7357Z\"\n fill=\"white\"\n />\n </svg>`;\n }\n\n render() {\n if (!this._isValidYouTubeUrl()) {\n return html``;\n }\n\n const embedUrl = this._getEmbedUrl();\n if (!embedUrl) return html``;\n\n // Use explicit size if set, otherwise fall back to auto-computed size\n const effectiveSize = this.size || \"md\";\n\n /**\n * Show thumbnail first, unless until user clicks or developer sets \"autoplay\".\n * Then, swap to iframe.\n */\n if (!this._playerActive && !this.autoplay) {\n return html`\n <div\n class=\"nys-video nys-video--${effectiveSize} ${this.disabled\n ? \"nys-video--disabled\"\n : \"\"}\"\n >\n ${this._renderAnnouncer()}\n <div class=\"nys-video__ratio-box\">\n <div\n class=\"nys-video__thumbnail\"\n @click=${this._handleThumbnailClick}\n >\n <img src=${this._getThumbnailUrl()} alt=\"\" />\n <button\n class=\"nys-video__play-icon\"\n aria-label=\"Play ${this.titleText}\"\n ?disabled=${this.disabled}\n >\n ${this._renderPlayIcon()}\n </button>\n </div>\n </div>\n ${this.titleText &&\n html`<div class=\"nys-video__title-text\">\n <p>${this.titleText}</p>\n </div>`}\n </div>\n `;\n }\n\n return html`<div class=\"nys-video nys-video--${effectiveSize}\">\n ${this._renderAnnouncer()}\n <div class=\"nys-video__ratio-box\">\n <iframe\n tabindex=\"0\"\n src=${embedUrl}\n title=${this.titleText}\n aria-label=${this.titleText}\n loading=${this.loading}\n allowfullscreen\n frameborder=\"0\"\n allow=\"accelerometer;autoplay;clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n ></iframe>\n </div>\n </div>`;\n }\n}\n\nif (!customElements.get(\"nys-video\")) {\n customElements.define(\"nys-video\", NysVideo);\n}\n"],"names":["videoIdCounter","_NysVideo","LitElement","regExp","match","videoId","params","setup","iframe","event","script","html","embedUrl","effectiveSize","unsafeCSS","styles","NysVideo","__decorateClass","property","state"],"mappings":";;;;;;;;;;;;;;;;;;AAYA,IAAIA,IAAiB;AAwBd,MAAMC,IAAN,MAAMA,UAAiBC,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA,EAqDvC,cAAc;AACZ,UAAA,GAlDyC,KAAA,KAAK,IAGL,KAAA,YAAY,IAG3B,KAAA,WAAW,IAMI,KAAA,OACzC,IAE0B,KAAA,UAA4B,QAM5B,KAAA,YAAY,GAMZ,KAAA,YAA2B,MAG1B,KAAA,WAAW,IAGI,KAAA,WAAW,IAG9C,KAAQ,gBAAgB,IAGxB,KAAQ,gBAAgB,IAGjC,KAAQ,aAAa;AAAA,EASrB;AAAA;AAAA,EAGA,oBAAoB;AAClB,UAAM,kBAAA,GACD,KAAK,OACR,KAAK,KAAK,aAAa,KAAK,KAAK,IAAIF,GAAgB,KAGnD,KAAK,YACP,KAAK,iBAAA;AAAA,EAET;AAAA,EAEA,uBAAuB;AACrB,UAAM,qBAAA;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,qBAA8B;AACpC,WAAO,sDAAsD;AAAA,MAC3D,KAAK;AAAA,IAAA;AAAA,EAET;AAAA,EAEQ,cAAc;AACpB,UAAMG,IACJ,wEACIC,IAAQ,KAAK,SAAS,MAAMD,CAAM;AACxC,WAAOC,IAAQA,EAAM,CAAC,IAAI;AAAA,EAC5B;AAAA,EAEQ,mBAAmB;AACzB,WAAI,KAAK,YAAkB,KAAK,YAEzB,8BADS,KAAK,YAAA,CACuB;AAAA,EAC9C;AAAA,EAEQ,eAAe;AACrB,UAAMC,IAAU,KAAK,YAAA;AACrB,QAAI,CAACA,EAAS,QAAO;AAErB,UAAMC,IAAS,IAAI,gBAAgB;AAAA,MACjC,UAAU;AAAA,MACV,aAAa;AAAA,MACb,GAAI,KAAK,YAAY,EAAE,MAAM,IAAA;AAAA,MAC7B,GAAI,KAAK,YAAY,KAAK,EAAE,OAAO,OAAO,KAAK,SAAS,EAAA;AAAA,IAAE,CAC3D;AAED,WAAO,iCAAiCD,CAAO,IAAIC,CAAM;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,mBAAmB;AACzB,UAAMC,IAAQ,MAAM;AAClB,WAAK,eAAe,KAAK,MAAM;AAC7B,cAAMC,IAAS,KAAK,YAAY,cAAc,QAAQ;AACtD,QAAKA,KAEL,IAAI,OAAO,GAAG,OAAOA,GAAQ;AAAA,UAC3B,QAAQ;AAAA,YACN,eAAe,CAACC,MAA4B;AAC1C,cAAI,KAAK,cAELA,EAAM,SAAS,OAAO,GAAG,YAAY,YACvC,KAAK,gBAAgB,KAAK,WACtB,4BACA,oBACJ,WAAW,MAAO,KAAK,gBAAgB,IAAK,GAAI;AAAA,YAEpD;AAAA;AAAA;AAAA,YAGA,iBAAiB,CAACA,MAA4B;AAC5C,mBAAK,aAAaA,EAAM,SAAS,OAAO,GAAG,YAAY,SAEnD,KAAK,eACP,KAAK,gBAAgB,4BACrB,WAAW,MAAO,KAAK,gBAAgB,IAAK,GAAI;AAAA,YAEpD;AAAA,UAAA;AAAA,QACF,CACD;AAAA,MACH,CAAC;AAAA,IACH;AAGA,QAAI,OAAO,IAAI;AACb,MAAAF,EAAA;AAAA,SACK;AACL,UAAI,CAAC,SAAS,eAAe,eAAe,GAAG;AAC7C,cAAMG,IAAS,SAAS,cAAc,QAAQ;AAC9C,QAAAA,EAAO,KAAK,iBACZA,EAAO,MAAM,sCACb,SAAS,KAAK,YAAYA,CAAM;AAAA,MAClC;AACA,aAAO,0BAA0BH;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,wBAAwB;AAC9B,IAAI,KAAK,aACT,KAAK,gBAAgB,IAErB,KAAK,eAAe,KAAK,MAAM;AAC7B,YAAMC,IAAS,KAAK,YAAY,cAAc,QAAQ;AACtD,MAAKA,KAELA,EAAO;AAAA,QACL;AAAA,QACA,MAAM;AACJ,UAAAA,EAAO,MAAA;AAAA,QACT;AAAA,QACA,EAAE,MAAM,GAAA;AAAA,MAAK;AAAA,IAEjB,CAAC,GAED,KAAK,iBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,mBAAmB;AACzB,WAAOG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAMD,KAAK,aAAa;AAAA;AAAA;AAAA,EAG1B;AAAA,EAEQ,kBAAkB;AACxB,WAAO,KAAK,WACRA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAcAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaN;AAAA,EAEA,SAAS;AACP,QAAI,CAAC,KAAK;AACR,aAAOA;AAGT,UAAMC,IAAW,KAAK,aAAA;AACtB,QAAI,CAACA,EAAU,QAAOD;AAGtB,UAAME,IAAgB,KAAK,QAAQ;AAMnC,WAAI,CAAC,KAAK,iBAAiB,CAAC,KAAK,WACxBF;AAAA;AAAA,wCAE2BE,CAAa,IAAI,KAAK,WAChD,wBACA,EAAE;AAAA;AAAA,YAEJ,KAAK,kBAAkB;AAAA;AAAA;AAAA;AAAA,uBAIZ,KAAK,qBAAqB;AAAA;AAAA,yBAExB,KAAK,kBAAkB;AAAA;AAAA;AAAA,mCAGb,KAAK,SAAS;AAAA,4BACrB,KAAK,QAAQ;AAAA;AAAA,kBAEvB,KAAK,iBAAiB;AAAA;AAAA;AAAA;AAAA,YAI5B,KAAK,aACPF;AAAA,iBACO,KAAK,SAAS;AAAA,iBACd;AAAA;AAAA,UAKNA,qCAAwCE,CAAa;AAAA,QACxD,KAAK,kBAAkB;AAAA;AAAA;AAAA;AAAA,gBAIfD,CAAQ;AAAA,kBACN,KAAK,SAAS;AAAA,uBACT,KAAK,SAAS;AAAA,oBACjB,KAAK,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9B;AACF;AA1SEX,EAAO,SAASa,EAAUC,CAAM;AAD3B,IAAMC,IAANf;AAIsCgB,EAAA;AAAA,EAA1CC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAJ9BF,EAIgC,WAAA,IAAA;AAGAC,EAAA;AAAA,EAA1CC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAP9BF,EAOgC,WAAA,WAAA;AAGfC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAVfF,EAUiB,WAAA,UAAA;AAMeC,EAAA;AAAA,EAA1CC,EAAS,EAAE,MAAM,QAAQ,SAAS,IAAM;AAAA,GAhB9BF,EAgBgC,WAAA,MAAA;AAGfC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAnBfF,EAmBiB,WAAA,SAAA;AAMAC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GAzBfF,EAyBiB,WAAA,WAAA;AAMAC,EAAA;AAAA,EAA3BC,EAAS,EAAE,MAAM,OAAA,CAAQ;AAAA,GA/BfF,EA+BiB,WAAA,WAAA;AAGCC,EAAA;AAAA,EAA5BC,EAAS,EAAE,MAAM,QAAA,CAAS;AAAA,GAlChBF,EAkCkB,WAAA,UAAA;AAGeC,EAAA;AAAA,EAA3CC,EAAS,EAAE,MAAM,SAAS,SAAS,IAAM;AAAA,GArC/BF,EAqCiC,WAAA,UAAA;AAG3BC,EAAA;AAAA,EAAhBE,EAAA;AAAM,GAxCIH,EAwCM,WAAA,eAAA;AAGAC,EAAA;AAAA,EAAhBE,EAAA;AAAM,GA3CIH,EA2CM,WAAA,eAAA;AAkQd,eAAe,IAAI,WAAW,KACjC,eAAe,OAAO,aAAaA,CAAQ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nysds/nys-video",
3
- "version": "1.16.1",
3
+ "version": "1.17.0",
4
4
  "description": "The Video Player component from the NYS Design System.",
5
5
  "module": "dist/nys-video.js",
6
6
  "types": "dist/index.d.ts",