diff --git a/src/components.d.ts b/src/components.d.ts index e3f7fb4a3c57cae77811cd701bc919f36d0af33a..1c10f0ae491a3333106db5ecc872709ff0856c49 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -517,6 +517,69 @@ export namespace Components { logoLabel?: string; logoUrl?: string; } + interface HyVideo { + /** + * Context label + */ + contextLabel: string; + /** + * Should context label be visible + */ + contextLabelVisible: boolean; + /** + * Date added date as string + */ + dateAdded: string; + /** + * Label for date added + */ + dateAddedLabel: string; + /** + * Duration number + */ + duration: string; + /** + * Label for duration number + */ + durationLabel: string; + headerstyle: string; + /** + * Use horizontal layout where metadata is on side + */ + horizontal: boolean; + /** + * Url to video, used with preview image + */ + linkToVideo: string; + /** + * Label for play button if preview image is provided + */ + playButtonLabel: string; + /** + * Should play button be visible + */ + playButtonVisible: boolean; + /** + * Preview image url + */ + previewImageUrl: string; + /** + * Video description + */ + videoDescription: string; + /** + * Video title + */ + videoTitle: string; + /** + * Views count number + */ + views: string; + /** + * Label for views number + */ + viewsLabel: string; + } } declare global { interface HTMLColorBoxElement extends Components.ColorBox, HTMLStencilElement {} @@ -829,6 +892,11 @@ declare global { prototype: HTMLHyUserLoginFormElement; new (): HTMLHyUserLoginFormElement; }; + interface HTMLHyVideoElement extends Components.HyVideo, HTMLStencilElement {} + var HTMLHyVideoElement: { + prototype: HTMLHyVideoElement; + new (): HTMLHyVideoElement; + }; interface HTMLElementTagNameMap { 'color-box': HTMLColorBoxElement; 'hy-accordion-container': HTMLHyAccordionContainerElement; @@ -892,6 +960,7 @@ declare global { 'hy-tiny-text': HTMLHyTinyTextElement; 'hy-two-columns': HTMLHyTwoColumnsElement; 'hy-user-login-form': HTMLHyUserLoginFormElement; + 'hy-video': HTMLHyVideoElement; } } declare namespace LocalJSX { @@ -1376,6 +1445,69 @@ declare namespace LocalJSX { logoLabel?: string; logoUrl?: string; } + interface HyVideo { + /** + * Context label + */ + contextLabel?: string; + /** + * Should context label be visible + */ + contextLabelVisible?: boolean; + /** + * Date added date as string + */ + dateAdded?: string; + /** + * Label for date added + */ + dateAddedLabel?: string; + /** + * Duration number + */ + duration?: string; + /** + * Label for duration number + */ + durationLabel?: string; + headerstyle?: string; + /** + * Use horizontal layout where metadata is on side + */ + horizontal?: boolean; + /** + * Url to video, used with preview image + */ + linkToVideo?: string; + /** + * Label for play button if preview image is provided + */ + playButtonLabel?: string; + /** + * Should play button be visible + */ + playButtonVisible?: boolean; + /** + * Preview image url + */ + previewImageUrl?: string; + /** + * Video description + */ + videoDescription?: string; + /** + * Video title + */ + videoTitle?: string; + /** + * Views count number + */ + views?: string; + /** + * Label for views number + */ + viewsLabel?: string; + } interface IntrinsicElements { 'color-box': ColorBox; 'hy-accordion-container': HyAccordionContainer; @@ -1439,6 +1571,7 @@ declare namespace LocalJSX { 'hy-tiny-text': HyTinyText; 'hy-two-columns': HyTwoColumns; 'hy-user-login-form': HyUserLoginForm; + 'hy-video': HyVideo; } } export {LocalJSX as JSX}; @@ -1511,6 +1644,7 @@ declare module '@stencil/core' { 'hy-tiny-text': LocalJSX.HyTinyText & JSXBase.HTMLAttributes<HTMLHyTinyTextElement>; 'hy-two-columns': LocalJSX.HyTwoColumns & JSXBase.HTMLAttributes<HTMLHyTwoColumnsElement>; 'hy-user-login-form': LocalJSX.HyUserLoginForm & JSXBase.HTMLAttributes<HTMLHyUserLoginFormElement>; + 'hy-video': LocalJSX.HyVideo & JSXBase.HTMLAttributes<HTMLHyVideoElement>; } } } diff --git a/src/components/heading/readme.md b/src/components/heading/readme.md index e02907796fa7ceb404ea60a4123de3a376c3315f..7e1092499b438f55ac541242b200f3ed4869804f 100644 --- a/src/components/heading/readme.md +++ b/src/components/heading/readme.md @@ -104,6 +104,7 @@ Provide heading attribute for the component to choose what type of heading to us - [hy-introduction](../hy-introduction) - [hy-large-process-flow](../hy-large-process-flow) - [hy-shortcuts](../hy-shortcuts) +- [hy-video](../hy-video) ### Graph @@ -115,6 +116,7 @@ graph TD; hy-introduction --> hy-heading hy-large-process-flow --> hy-heading hy-shortcuts --> hy-heading + hy-video --> hy-heading style hy-heading fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/src/components/hy-video/hy-video.scss b/src/components/hy-video/hy-video.scss new file mode 100644 index 0000000000000000000000000000000000000000..839e39d6967d0c1c98ca691417698555eb08e56b --- /dev/null +++ b/src/components/hy-video/hy-video.scss @@ -0,0 +1,271 @@ +:host { + display: block; +} + +.hy-video__video-container { + display: inline-block; + margin-bottom: 8px; + position: relative; + width: 100%; + + @include breakpoint($wide) { + margin-bottom: 16px; + } + + iframe { + height: 100%; + overflow: hidden; + width: 100%; + } + + .hy-video__preview-image { + height: 100%; + left: 0; + object-fit: cover; + position: absolute; + top: 0; + width: 100%; + } + + .hy-video__play { + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%); + + button { + align-items: center; + background: transparent; + border: none; + display: flex; + flex-direction: column; + + &:hover { + cursor: pointer; + } + } + + button .hy-video__button-label { + @include font-size(16px, 24px); + + color: var(--grayscale-white); + font-family: var(--main-font-family); + font-weight: bold; + letter-spacing: -0.1px; + text-align: center; + text-shadow: 0 0 8px rgba(0, 0, 0, 0.6); + text-transform: uppercase; + + @include breakpoint($medium) { + @include font-size(24px, 32px); + + letter-spacing: -0.14px; + text-shadow: 0 0 16px rgba(0, 0, 0, 0.6); + } + + @include breakpoint($extrawide) { + @include font-size(28px, 32px); + + letter-spacing: -0.16px; + } + @include breakpoint($overwide) { + @include font-size(32px, 32px); + } + } + + button svg { + fill: var(--grayscale-white); + filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.6)); + height: 48px; + width: 48px; + + @include breakpoint($medium) { + filter: drop-shadow(0 0 16px rgba(0, 0, 0, 0.6)); + height: 60px; + width: 60px; + } + @include breakpoint($wide) { + height: 80px; + width: 80px; + } + @include breakpoint($extrawide) { + height: 104px; + width: 104px; + } + @include breakpoint($overwide) { + height: 120px; + width: 120px; + } + } + } + + > *:not(.hy-video__label):not(.hy-video__play) { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } + + &:after { + content: ''; + display: block; + // 16:9 ratio + padding-top: (9 / 16) * 100%; + } +} + +.hy-video:not(.hy-video--horizontal) { + display: flex; + flex-direction: column; + + .hy-video__meta__description { + @include breakpoint($wide) { + width: 57.24%; + } + } +} + +.hy-video__label { + // font: h5 small size + @include font-size(16px, 20px); + background-color: var(--grayscale-black); + bottom: 12px; + color: var(--grayscale-white); + font-family: var(--main-font-family); + font-weight: bold; + left: -8px; + letter-spacing: -0.5px; + max-width: 46%; // 5.5 columns + padding: 6px 12px; + position: absolute; + text-transform: uppercase; + + @include breakpoint($narrow) { + // >=480px + // font: h4 small size + @include font-size(18px, 24px); + bottom: 20px; + left: -16px; + letter-spacing: -0.56px; + max-width: 58%; // 7 columns + padding: 12px 24px; + position: absolute; + } + + @include breakpoint($wide) { + // Applies both to 960px-1200px and 1201-1440px + // font: h4 medium size + @include font-size(22px, 28px); + bottom: 24px; + left: -16px; + letter-spacing: -0.69px; + padding: 16px 24px; + position: absolute; + } + @include breakpoint($overwide) { + // >= 1441px + // font: h3 medium size + @include font-size(26px, 32px); + bottom: 28px; + left: -16px; + letter-spacing: -0.8px; + padding: 20px 24px; + position: absolute; + } +} + +.hy-video.hy-video--horizontal { + display: flex; + flex-direction: column; + justify-content: space-between; + + @include breakpoint($wide) { + flex-direction: row; + } + + .hy-video__meta { + padding-left: 0; + width: 100%; + + @include breakpoint($wide) { + padding-left: 2.5%; + width: 57.24%; + } + } +} + +.hy-video__meta__title { + h2.hy-heading { + margin: 8px 0 12px 0 !important; + padding: 0 !important; + + @include breakpoint($wide) { + margin: 0 0 16px 0 !important; + } + } +} + +.hy-video__meta__details { + display: flex; + flex-direction: row; + margin-bottom: 12px; + + @include breakpoint($wide) { + margin-bottom: 16px; + } +} + +.hy-video__meta__details--item { + border-right: 1px dashed var(--brand-main-light); + display: flex; + flex-direction: column; + margin-right: 44px; + min-width: 87px; + padding-right: 46px; + place-content: center space-between; + + &:last-child { + border-right: 0; + } +} + +.hy-video__meta__description { + @include font-size(14px, 20px); + + color: var(--grayscale-dark); + display: block; + font-family: var(--main-font-family); + letter-spacing: 0; + margin-bottom: 16px; + width: 100%; + + @include breakpoint($medium) { + @include font-size(16px, 24px); + } +} + +.hy-video__meta__label { + font-weight: 700; +} + +.hy-video__meta__label, +.hy-video__meta__content { + @include font-size(16px, 20px); + + color: var(--grayscale-black); + font-family: var(--main-font-family); + letter-spacing: -0.5px; + + @include breakpoint($medium) { + @include font-size(18px, 24px); + + letter-spacing: -0.56px; + } + + @include breakpoint($extrawide) { + @include font-size(22px, 34px); + + letter-spacing: -0.69px; + } +} diff --git a/src/components/hy-video/hy-video.tsx b/src/components/hy-video/hy-video.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9726f5e07e4a3a24f87eca54ff881e0821b3ff15 --- /dev/null +++ b/src/components/hy-video/hy-video.tsx @@ -0,0 +1,122 @@ +import {Component, Prop, h, Element} from '@stencil/core'; +import {HeadingVarians} from '../../utils/utils'; + +@Component({ + tag: 'hy-video', + styleUrl: 'hy-video.scss', + shadow: false, +}) +export class HyVideo { + @Element() el: HTMLElement; + + /** Video title */ + @Prop() videoTitle: string; + /** Video description */ + @Prop() videoDescription: string; + /** Context label */ + @Prop() contextLabel: string; + /** Should context label be visible */ + @Prop() contextLabelVisible: boolean = false; + /** Preview image url */ + @Prop() previewImageUrl: string; + /** Url to video, used with preview image */ + @Prop() linkToVideo: string; + /** Label for play button if preview image is provided */ + @Prop() playButtonLabel: string; + /** Should play button be visible */ + @Prop() playButtonVisible: boolean = false; + /** Views count number */ + @Prop() views: string; + /** Label for views number */ + @Prop() viewsLabel: string; + /** Duration number */ + @Prop() duration: string; + /** Label for duration number */ + @Prop() durationLabel: string; + /** Date added date as string */ + @Prop() dateAdded: string; + /** Label for date added */ + @Prop() dateAddedLabel: string; + /** Use horizontal layout where metadata is on side */ + @Prop() horizontal: boolean = false; + + @Prop() headerstyle: string = 'with-sidebar'; + + renderPreviewWithLink = () => { + return this.linkToVideo ? ( + <a title={this.videoTitle} href={this.linkToVideo} class="hy-video__link-to-video"> + <img class="hy-video__preview-image" src={this.previewImageUrl} alt={this.videoTitle} /> + </a> + ) : ( + <img class="hy-video__preview-image" src={this.previewImageUrl} alt={this.videoTitle} /> + ); + }; + + renderPlayButton = () => { + return ( + <span class="hy-video__play"> + <button type="button"> + <hy-icon icon={'hy-icon-video'} class={'hy-video__play-icon'} size={20} /> + <span class="hy-video__button-label">{this.playButtonLabel}</span> + </button> + </span> + ); + }; + + render() { + const classAttributes = [ + 'hy-video', + this.horizontal ? 'hy-video--horizontal' : '', + `hy-video--${this.headerstyle}`, + ].join(' '); + + return ( + <div class={classAttributes}> + <div class="hy-video__video-container"> + {this.previewImageUrl ? ( + <div> + {this.renderPreviewWithLink()} + <slot name="video"></slot> + </div> + ) : ( + <slot name="video"></slot> + )} + {this.playButtonVisible && this.renderPlayButton()} + {this.contextLabelVisible && <span class="hy-video__label">{this.contextLabel}</span>} + </div> + <div class="hy-video__meta"> + {this.videoTitle && ( + <hy-heading class="hy-video__meta__title" heading={HeadingVarians.h2}> + {this.videoTitle} + </hy-heading> + )} + {(this.duration || this.views || this.dateAdded) && ( + <div class="hy-video__meta__details"> + {this.duration && ( + <div class="hy-video__meta__details--item"> + <span class="hy-video__meta__label">{this.durationLabel}</span> + <span class="hy-video__meta__content">{this.duration}</span> + </div> + )} + {this.views && ( + <div class="hy-video__meta__details--item"> + <span class="hy-video__meta__label">{this.viewsLabel}</span> + <span class="hy-video__meta__content">{this.views}</span> + </div> + )} + {this.dateAdded && ( + <div class="hy-video__meta__details--item"> + <span class="hy-video__meta__label">{this.dateAddedLabel}</span> + <span class="hy-video__meta__content">{this.dateAdded}</span> + </div> + )} + </div> + )} + {this.videoDescription && ( + <hy-paragraph-text class="hy-video__meta__description">{this.videoDescription}</hy-paragraph-text> + )} + </div> + </div> + ); + } +} diff --git a/src/components/hy-video/readme.md b/src/components/hy-video/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..b8df742528989a4bbbdfe871993f11c31453d283 --- /dev/null +++ b/src/components/hy-video/readme.md @@ -0,0 +1,46 @@ +# hy-video + +<!-- Auto Generated Below --> + +## Properties + +| Property | Attribute | Description | Type | Default | +| --------------------- | ----------------------- | -------------------------------------------------- | --------- | ---------------- | +| `contextLabel` | `context-label` | Context label | `string` | `undefined` | +| `contextLabelVisible` | `context-label-visible` | Should context label be visible | `boolean` | `false` | +| `dateAdded` | `date-added` | Date added date as string | `string` | `undefined` | +| `dateAddedLabel` | `date-added-label` | Label for date added | `string` | `undefined` | +| `duration` | `duration` | Duration number | `string` | `undefined` | +| `durationLabel` | `duration-label` | Label for duration number | `string` | `undefined` | +| `headerstyle` | `headerstyle` | | `string` | `'with-sidebar'` | +| `horizontal` | `horizontal` | Use horizontal layout where metadata is on side | `boolean` | `false` | +| `linkToVideo` | `link-to-video` | Url to video, used with preview image | `string` | `undefined` | +| `playButtonLabel` | `play-button-label` | Label for play button if preview image is provided | `string` | `undefined` | +| `playButtonVisible` | `play-button-visible` | Should play button be visible | `boolean` | `false` | +| `previewImageUrl` | `preview-image-url` | Preview image url | `string` | `undefined` | +| `videoDescription` | `video-description` | Video description | `string` | `undefined` | +| `videoTitle` | `video-title` | Video title | `string` | `undefined` | +| `views` | `views` | Views count number | `string` | `undefined` | +| `viewsLabel` | `views-label` | Label for views number | `string` | `undefined` | + +## Dependencies + +### Depends on + +- [hy-icon](../icon) +- [hy-heading](../heading) +- [hy-paragraph-text](../paragraph-text) + +### Graph + +```mermaid +graph TD; + hy-video --> hy-icon + hy-video --> hy-heading + hy-video --> hy-paragraph-text + style hy-video fill:#f9f,stroke:#333,stroke-width:4px +``` + +--- + +Helsinki University Design System diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index ac28adb441f75a73a07c844c0014e997c6cb0368..015304d4aaa5aabe151ea8f8fd5f603065aabbc3 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -23,6 +23,8 @@ const iconNames: IconName = { 'hy-icon-quote': (p) => <icons.IconQuote {...p} />, 'hy-icon-remove': (p) => <icons.Remove {...p} />, 'hy-icon-search': (p) => <icons.Search {...p} />, + 'hy-icon-play': (p) => <icons.Play {...p} />, + 'hy-icon-video': (p) => <icons.Video {...p} />, 'hy-icon-some-facebook': (p) => <icons.SomeFacebook {...p} />, 'hy-icon-some-instagram': (p) => <icons.SomeInstagram {...p} />, 'hy-icon-some-youtube': (p) => <icons.SomeYoutube {...p} />, diff --git a/src/components/icon/readme.md b/src/components/icon/readme.md index 82e396ca8c7f096fe8db7b0202ec8d1ae892407c..3f1bef04db8e8c5dea490a68bdea9e14342af52e 100644 --- a/src/components/icon/readme.md +++ b/src/components/icon/readme.md @@ -34,6 +34,7 @@ - [hy-site-logo](../site-header/site-logo) - [hy-site-search](../site-header/site-search) - [hy-tabs](../hy-tabs) +- [hy-video](../hy-video) ### Graph @@ -59,6 +60,7 @@ graph TD; hy-site-logo --> hy-icon hy-site-search --> hy-icon hy-tabs --> hy-icon + hy-video --> hy-icon style hy-icon fill:#f9f,stroke:#333,stroke-width:4px ``` diff --git a/src/components/paragraph-text/readme.md b/src/components/paragraph-text/readme.md index 7ad35e36fa50f80010a4e378f8973461f0b2ce5a..9ae4383ffec8f5816666e481fc7374912239d97c 100644 --- a/src/components/paragraph-text/readme.md +++ b/src/components/paragraph-text/readme.md @@ -36,6 +36,20 @@ Basic text paragraph. | `headerstyle` | `headerstyle` | | `string` | `'default'` | | `variant` | `variant` | | `string` | `undefined` | +## Dependencies + +### Used by + +- [hy-video](../hy-video) + +### Graph + +```mermaid +graph TD; + hy-video --> hy-paragraph-text + style hy-paragraph-text fill:#f9f,stroke:#333,stroke-width:4px +``` + --- Helsinki University Design System diff --git a/src/index.html b/src/index.html index 5f6fc07d9a1b07782d406ae7db5a18ca7696b230..3967c93eacfb5129b108d7396c5cb294d86f013e 100644 --- a/src/index.html +++ b/src/index.html @@ -186,6 +186,62 @@ sc-label="List of conferences" > </hy-banner> + <hy-video + video-title="Youtube test embed" + video-description="Video description for giving more context to what the user is viewing (eg. if the video is part of a lecture series, or a series of speeches). Can include links and probably needs to have a character limit." + context-label="Youtube test" + views="123" + views-label="Views" + duration="1:23" + duration-label="Duration" + date-added="28.10.2020" + date-added-label="Added" + play-button-visible + context-label-visible + play-button-label="Play video" + preview-image-url="https://www.helsinki.fi/sites/default/files/styles/16_9_huge/public/kukkataedit_ja_-sedaet-6_0.jpg" + > + <iframe + slot="video" + src="https://www.youtube.com/embed/Bsycp5I2Vy8?width=&height=&theme=dark&autoplay=0&vq=hd720&rel=0&showinfo=1&modestbranding=0&iv_load_policy=1&controls=1&autohide=2&wmode=opaque" + frameborder="0" + ></iframe> + </hy-video> + <hy-video + horizontal + video-title="Youtube test embed" + video-description="Video description for giving more context to what the user is viewing (eg. if the video is part of a lecture series, or a series of speeches). Can include links and probably needs to have a character limit." + context-label="Youtube test" + views="123" + views-label="Views" + duration="1:23" + context-label-visible + duration-label="Duration" + date-added="28.10.2020" + date-added-label="Added" + > + <iframe + slot="video" + src="https://www.youtube.com/embed/Bsycp5I2Vy8?width=&height=&theme=dark&autoplay=0&vq=hd720&rel=0&showinfo=1&modestbranding=0&iv_load_policy=1&controls=1&autohide=2&wmode=opaque" + frameborder="0" + ></iframe> + </hy-video> + <hy-video + context-label="Youtube test" + play-button-visible + context-label-visible + play-button-label="Play video" + link-to-video="https://www.google.com" + preview-image-url="https://www.helsinki.fi/sites/default/files/styles/16_9_huge/public/kukkataedit_ja_-sedaet-6_0.jpg" + > + </hy-video> + <hy-video> + <iframe + slot="video" + src="https://www.youtube.com/embed/Bsycp5I2Vy8?width=&height=&theme=dark&autoplay=0&vq=hd720&rel=0&showinfo=1&modestbranding=0&iv_load_policy=1&controls=1&autohide=2&wmode=opaque" + frameborder="0" + ></iframe> + </hy-video> <hy-accordion-container accordionid="example-accordion"> <hy-accordion-item accordiontitle="This is a accordion item 1"> <hy-paragraph-text>Accordion content</hy-paragraph-text>