diff --git a/README.md b/README.md index 09b48ff..a41421b 100644 --- a/README.md +++ b/README.md @@ -75,10 +75,66 @@ use `topsort-banner-slot` as children elements. | location | Optional String | The location for geotargeting | | new-tab | Optional Boolean | Opens the banner's link in a new tab (defaults to false) | | context | Optional Boolean | Uses the element as a context provider to render multiple banners | +| class | Optional String | Custom CSS class to apply to the banner container | \* Only one of `[category-id, category-ids, category-disjunctions]` must be set. If multiple are set, only the first will be considered, in that order. +# Styling + +The banner component is designed to integrate seamlessly with your existing CSS system. +Each banner is rendered inside a container div with the class `ts-banner`, +making it easy to target with CSS selectors. + +## CSS Targeting + +You can style banners using standard CSS selectors: + +```css +/* Target all banner containers */ +.ts-banner { + padding: 10px; + margin: 10px; + border: 1px solid #ccc; +} + +/* Target banners with specific dimensions */ +.ts-banner[data-ts-width="800"] { + max-width: 800px; +} +``` + +## Custom CSS Classes + +You can pass custom CSS classes to the banner component using the `class` attribute: + +```html + +``` + +The custom class will be applied alongside the `ts-banner` class, allowing you to +integrate the banner into your existing CSS system. + +## Retrieving Width and Height + +The width and height values are easily accessible via: + +- **Data attributes**: `data-ts-width` and `data-ts-height` on the container element +- **CSS custom properties**: `--ts-banner-width` and `--ts-banner-height` on the container element + +```css +/* Use data attributes in CSS */ +.ts-banner[data-ts-width] { + /* Styles for banners with width set */ +} + +/* Use CSS custom properties */ +.ts-banner { + width: var(--ts-banner-width, 100%); + height: var(--ts-banner-height, auto); +} +``` + # Banner Slot Attributes | Name | Type | Description | |------|--------|-----------------------------------------------------------------------------------------------------------------------| diff --git a/index.html b/index.html index 50ac9c7..c5acacc 100644 --- a/index.html +++ b/index.html @@ -43,6 +43,13 @@ justify-content: center; padding: 20px; } + .ts-banner { + padding: 10px; + margin: 10px; + } + .ts-banner[data-ts-width="800"] { + border: 1px solid #ddd; + }
@@ -52,11 +59,11 @@

Standalone Banner

<topsort-banner id="an-example-slot" width="800" height="400"></topsort-banner> -
+
-
+

Multiple Banners under one context

         
diff --git a/src/index.ts b/src/index.ts
index 4d1457e..3c725a3 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -73,9 +73,10 @@ function getNoWinnersElement(): TemplateResult {
 
 function getBannerElement(
   banner: Banner,
-  width: number,
-  height: number,
   newTab: boolean,
+  width?: number,
+  height?: number,
+  bannerClass?: string,
 ): TemplateResult {
   if (window.TS_BANNERS.getBannerElement) {
     const element = window.TS_BANNERS.getBannerElement(banner);
@@ -102,19 +103,39 @@ function getBannerElement(
       return false;
     }
   })();
+
+  const containerClass = bannerClass ? `ts-banner ${bannerClass}` : "ts-banner";
+
+  const containerStyle = [
+    "display: block",
+    width ? `--ts-banner-width: ${width}px` : "",
+    height ? `--ts-banner-height: ${height}px` : "",
+  ]
+    .filter(Boolean)
+    .join("; ");
+
+  // let CSS cascade
+  const mediaStyle =
+    width && height
+      ? `width: ${width}px; height: ${height}px; object-fit: cover;`
+      : width
+        ? `width: ${width}px; height: auto; object-fit: cover;`
+        : height
+          ? `width: 100%; height: ${height}px; object-fit: cover;`
+          : "width: 100%; height: auto; object-fit: cover;";
+
   const media = isVideo
     ? html`
         
       `
     : html`
         Topsort banner
       `;
 
@@ -124,9 +145,12 @@ function getBannerElement(
     : html`${media}`;
   return html`
     
${wrappedMedia}
@@ -145,6 +169,7 @@ const bannerContextHasChanged = (newVal: BannerContext, oldVal?: BannerContext) newVal.width !== oldVal.width || newVal.height !== oldVal.height || newVal.newTab !== oldVal.newTab || + newVal.bannerClass !== oldVal.bannerClass || !!newVal.error !== !!oldVal.error || newVal.banners?.length !== oldVal.banners?.length ); @@ -181,6 +206,7 @@ export class TopsortBanner extends BannerComponent(LitElement) { width: this.width, height: this.height, newTab: this.newTab, + bannerClass: this.bannerClass, }; @property({ type: Boolean, attribute: "context" }) @@ -203,7 +229,7 @@ export class TopsortBanner extends BannerComponent(LitElement) { if (!banners.length) { return getNoWinnersElement(); } - return getBannerElement(banners[0], this.width, this.height, this.newTab); + return getBannerElement(banners[0], this.newTab, this.width, this.height, this.bannerClass); }, error: (error) => getErrorElement(error), }); @@ -221,13 +247,15 @@ export class TopsortBanner extends BannerComponent(LitElement) { if ( changedProperties.has("width") || changedProperties.has("height") || - changedProperties.has("newTab") + changedProperties.has("newTab") || + changedProperties.has("bannerClass") ) { Promise.resolve().then(() => { this.context = { width: this.width, height: this.height, newTab: this.newTab, + bannerClass: this.bannerClass, }; }); } @@ -263,9 +291,10 @@ export class TopsortBannerSlot extends LitElement { } return getBannerElement( this.context.banners[this.rank - 1], + this.context.newTab, this.context.width, this.context.height, - this.context.newTab, + this.context.bannerClass, ); } @@ -278,8 +307,7 @@ export class TopsortBannerSlot extends LitElement { @customElement("hls-video") export class HlsVideo extends LitElement { @property({ type: String }) src = ""; // HLS manifest URL - @property({ type: String }) width = "800px"; - @property({ type: String }) height = "400px"; + @property({ type: String }) styles = ""; private get videoId() { try { @@ -297,6 +325,7 @@ export class HlsVideo extends LitElement { autoplay loop playsinline + style=${this.styles} > `; } @@ -305,10 +334,6 @@ export class HlsVideo extends LitElement { const video = this.shadowRoot?.getElementById(this.videoId) as HTMLVideoElement; if (!video) return; - video.style.width = this.width; - video.style.height = this.height; - video.style.objectFit = "cover"; - let Hls: HlsConstructor; try { Hls = await hlsDependency.load(); diff --git a/src/mixin.ts b/src/mixin.ts index 6c9f619..6ee9e37 100644 --- a/src/mixin.ts +++ b/src/mixin.ts @@ -16,6 +16,7 @@ export declare class BannerComponentInterface { searchQuery?: string; location?: string; newTab: boolean; + bannerClass?: string; emitEvent(status: string): void; buildAuction(slots: number): Auction; @@ -50,6 +51,9 @@ export const BannerComponent = >(Base: T) => { @property({ attribute: "new-tab", type: Boolean }) readonly newTab: boolean = false; + @property({ attribute: "class", type: String }) + readonly bannerClass?: string; + buildAuction(slots: number): Auction { const device = getDeviceType(); const auction: Auction = { diff --git a/src/types.ts b/src/types.ts index 709edfa..6ff618b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,7 @@ export interface BannerContext { width: number; height: number; newTab: boolean; + bannerClass?: string; banners?: Banner[]; error?: unknown; }