mirror of https://github.com/ghostfolio/ghostfolio
				
				
			
				 17 changed files with 486 additions and 198 deletions
			
			
		| @ -1 +1,2 @@ | |||
| buy_me_a_coffee: ghostfolio | |||
| github: ghostfolio | |||
|  | |||
| @ -1,3 +1,19 @@ | |||
| :host { | |||
|   display: block; | |||
| 
 | |||
|   .safe-withdrawal-rate-select { | |||
|     background-color: transparent; | |||
|     color: rgb(var(--dark-primary-text)); | |||
| 
 | |||
|     &:focus { | |||
|       box-shadow: none; | |||
|       outline: 0; | |||
|     } | |||
|   } | |||
| } | |||
| 
 | |||
| :host-context(.theme-dark) { | |||
|   .safe-withdrawal-rate-select { | |||
|     color: rgb(var(--light-primary-text)); | |||
|   } | |||
| } | |||
|  | |||
| @ -0,0 +1 @@ | |||
| export * from './logo-carousel.component'; | |||
| @ -0,0 +1,7 @@ | |||
| export interface LogoItem { | |||
|   className: string; | |||
|   isMask?: boolean; | |||
|   name: string; | |||
|   title: string; | |||
|   url: string; | |||
| } | |||
| @ -0,0 +1,16 @@ | |||
| <div class="logo-carousel-container overflow-hidden position-relative w-100"> | |||
|   <div class="align-items-center d-flex logo-carousel-track"> | |||
|     @for (logo of logosRepeated; track $index) { | |||
|       <div class="logo-carousel-item"> | |||
|         <a | |||
|           class="d-block logo" | |||
|           target="_blank" | |||
|           [attr.aria-label]="logo.name" | |||
|           [class]="logo.className + (logo.isMask ? ' mask' : '')" | |||
|           [href]="logo.url" | |||
|           [title]="logo.title" | |||
|         ></a> | |||
|       </div> | |||
|     } | |||
|   </div> | |||
| </div> | |||
| @ -0,0 +1,213 @@ | |||
| :host { | |||
|   display: block; | |||
|   overflow: hidden; | |||
|   position: relative; | |||
|   width: 100%; | |||
| 
 | |||
|   .logo-carousel-container { | |||
|     &::before, | |||
|     &::after { | |||
|       content: ''; | |||
|       height: 100%; | |||
|       pointer-events: none; | |||
|       position: absolute; | |||
|       top: 0; | |||
|       width: 100px; | |||
|       z-index: 2; | |||
|     } | |||
| 
 | |||
|     &::before { | |||
|       background: linear-gradient( | |||
|         to right, | |||
|         var(--light-background) 0%, | |||
|         rgba(var(--palette-background-background), 0) 100% | |||
|       ); | |||
|       left: 0; | |||
|     } | |||
| 
 | |||
|     &::after { | |||
|       background: linear-gradient( | |||
|         to left, | |||
|         var(--light-background) 0%, | |||
|         rgba(var(--palette-background-background), 0) 100% | |||
|       ); | |||
|       right: 0; | |||
|     } | |||
| 
 | |||
|     @media (max-width: 768px) { | |||
|       &::before, | |||
|       &::after { | |||
|         width: 50px; | |||
|       } | |||
|     } | |||
| 
 | |||
|     @media (max-width: 576px) { | |||
|       &::before, | |||
|       &::after { | |||
|         width: 30px; | |||
|       } | |||
|     } | |||
| 
 | |||
|     .logo-carousel-track { | |||
|       animation: scroll 60s linear infinite; | |||
|       width: fit-content; | |||
| 
 | |||
|       &:hover { | |||
|         animation-play-state: paused; | |||
|       } | |||
| 
 | |||
|       .logo-carousel-item { | |||
|         flex-shrink: 0; | |||
|         min-width: 200px; | |||
|         padding: 0 2rem; | |||
| 
 | |||
|         @media (max-width: 768px) { | |||
|           min-width: 150px; | |||
|           padding: 0 1.5rem; | |||
|         } | |||
| 
 | |||
|         @media (max-width: 576px) { | |||
|           min-width: 120px; | |||
|           padding: 0 1rem; | |||
|         } | |||
| 
 | |||
|         .logo { | |||
|           height: 3rem; | |||
|           transition: | |||
|             opacity 0.3s ease, | |||
|             transform 0.3s ease; | |||
|           width: 7.5rem; | |||
| 
 | |||
|           &:hover { | |||
|             opacity: 0.8; | |||
|           } | |||
| 
 | |||
|           &.mask { | |||
|             background-color: rgba(var(--dark-secondary-text)); | |||
|             mask-position: center; | |||
|             mask-repeat: no-repeat; | |||
|             mask-size: contain; | |||
|           } | |||
| 
 | |||
|           &.logo-alternative-to { | |||
|             mask-image: url('/assets/images/logo-alternative-to.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-awesome { | |||
|             background-image: url('/assets/images/logo-awesome.png'); | |||
|             background-position: center; | |||
|             background-repeat: no-repeat; | |||
|             background-size: contain; | |||
|             filter: grayscale(1); | |||
|           } | |||
| 
 | |||
|           &.logo-dev-community { | |||
|             mask-image: url('/assets/images/logo-dev-community.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-hacker-news { | |||
|             mask-image: url('/assets/images/logo-hacker-news.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-openalternative { | |||
|             mask-image: url('/assets/images/logo-openalternative.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-privacy-tools { | |||
|             mask-image: url('/assets/images/logo-privacy-tools.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-product-hunt { | |||
|             background-image: url('/assets/images/logo-product-hunt.png'); | |||
|             background-position: center; | |||
|             background-repeat: no-repeat; | |||
|             background-size: contain; | |||
|             filter: grayscale(1); | |||
|           } | |||
| 
 | |||
|           &.logo-reddit { | |||
|             mask-image: url('/assets/images/logo-reddit.svg'); | |||
|             max-height: 1rem; | |||
|           } | |||
| 
 | |||
|           &.logo-sackgeld { | |||
|             mask-image: url('/assets/images/logo-sackgeld.png'); | |||
|           } | |||
| 
 | |||
|           &.logo-selfh-st { | |||
|             mask-image: url('/assets/images/logo-selfh-st.svg'); | |||
|             max-height: 1.25rem; | |||
|           } | |||
| 
 | |||
|           &.logo-sourceforge { | |||
|             mask-image: url('/assets/images/logo-sourceforge.svg'); | |||
|           } | |||
| 
 | |||
|           &.logo-umbrel { | |||
|             mask-image: url('/assets/images/logo-umbrel.svg'); | |||
|             max-height: 1.5rem; | |||
|           } | |||
| 
 | |||
|           &.logo-unraid { | |||
|             mask-image: url('/assets/images/logo-unraid.svg'); | |||
|           } | |||
| 
 | |||
|           @media (max-width: 768px) { | |||
|             height: 2.5rem; | |||
|             width: 6rem; | |||
|           } | |||
| 
 | |||
|           @media (max-width: 576px) { | |||
|             height: 2rem; | |||
|             width: 5rem; | |||
|           } | |||
|         } | |||
|       } | |||
| 
 | |||
|       @keyframes scroll { | |||
|         0% { | |||
|           transform: translateX(0); | |||
|         } | |||
|         100% { | |||
|           transform: translateX(-50%); | |||
|         } | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| 
 | |||
| :host-context(.theme-dark) { | |||
|   .logo-carousel-container { | |||
|     &::before { | |||
|       background: linear-gradient( | |||
|         to right, | |||
|         var(--dark-background) 0%, | |||
|         rgba(var(--palette-background-background-dark), 0) 100% | |||
|       ); | |||
|     } | |||
| 
 | |||
|     &::after { | |||
|       background: linear-gradient( | |||
|         to left, | |||
|         var(--dark-background) 0%, | |||
|         rgba(var(--palette-background-background-dark), 0) 100% | |||
|       ); | |||
|     } | |||
| 
 | |||
|     .logo { | |||
|       &.logo-alternative-to, | |||
|       &.logo-dev-community, | |||
|       &.logo-hacker-news, | |||
|       &.logo-openalternative, | |||
|       &.logo-privacy-tools, | |||
|       &.logo-reddit, | |||
|       &.logo-sackgeld, | |||
|       &.logo-selfh-st, | |||
|       &.logo-sourceforge, | |||
|       &.logo-umbrel, | |||
|       &.logo-unraid { | |||
|         background-color: rgba(var(--light-primary-text)); | |||
|       } | |||
|     } | |||
|   } | |||
| } | |||
| @ -0,0 +1,13 @@ | |||
| import type { Meta, StoryObj } from '@storybook/angular'; | |||
| 
 | |||
| import { GfLogoCarouselComponent } from './logo-carousel.component'; | |||
| 
 | |||
| const meta: Meta<GfLogoCarouselComponent> = { | |||
|   title: 'Logo Carousel', | |||
|   component: GfLogoCarouselComponent | |||
| }; | |||
| 
 | |||
| export default meta; | |||
| type Story = StoryObj<GfLogoCarouselComponent>; | |||
| 
 | |||
| export const Default: Story = {}; | |||
| @ -0,0 +1,110 @@ | |||
| import { CommonModule } from '@angular/common'; | |||
| import { ChangeDetectionStrategy, Component } from '@angular/core'; | |||
| 
 | |||
| import { LogoItem } from './interfaces/interfaces'; | |||
| 
 | |||
| @Component({ | |||
|   changeDetection: ChangeDetectionStrategy.OnPush, | |||
|   imports: [CommonModule], | |||
|   selector: 'gf-logo-carousel', | |||
|   styleUrls: ['./logo-carousel.component.scss'], | |||
|   templateUrl: './logo-carousel.component.html' | |||
| }) | |||
| export class GfLogoCarouselComponent { | |||
|   public readonly logos: LogoItem[] = [ | |||
|     { | |||
|       className: 'logo-alternative-to', | |||
|       isMask: true, | |||
|       name: 'AlternativeTo', | |||
|       title: 'AlternativeTo - Crowdsourced software recommendations', | |||
|       url: 'https://alternativeto.net' | |||
|     }, | |||
|     { | |||
|       className: 'logo-awesome', | |||
|       name: 'Awesome Selfhosted', | |||
|       title: | |||
|         'Awesome-Selfhosted: A list of Free Software network services and web applications which can be hosted on your own servers', | |||
|       url: 'https://github.com/awesome-selfhosted/awesome-selfhosted' | |||
|     }, | |||
|     { | |||
|       className: 'logo-dev-community', | |||
|       isMask: true, | |||
|       name: 'DEV Community', | |||
|       title: | |||
|         'DEV Community - A constructive and inclusive social network for software developers', | |||
|       url: 'https://dev.to' | |||
|     }, | |||
|     { | |||
|       className: 'logo-hacker-news', | |||
|       isMask: true, | |||
|       name: 'Hacker News', | |||
|       title: 'Hacker News', | |||
|       url: 'https://news.ycombinator.com' | |||
|     }, | |||
|     { | |||
|       className: 'logo-openalternative', | |||
|       isMask: true, | |||
|       name: 'OpenAlternative', | |||
|       title: 'OpenAlternative: Open Source Alternatives to Popular Software', | |||
|       url: 'https://openalternative.co' | |||
|     }, | |||
|     { | |||
|       className: 'logo-privacy-tools', | |||
|       isMask: true, | |||
|       name: 'Privacy Tools', | |||
|       title: 'Privacy Tools: Software Alternatives and Encryption', | |||
|       url: 'https://www.privacytools.io' | |||
|     }, | |||
|     { | |||
|       className: 'logo-product-hunt', | |||
|       name: 'Product Hunt', | |||
|       title: 'Product Hunt – The best new products in tech.', | |||
|       url: 'https://www.producthunt.com' | |||
|     }, | |||
|     { | |||
|       className: 'logo-reddit', | |||
|       isMask: true, | |||
|       name: 'Reddit', | |||
|       title: 'Reddit - Dive into anything', | |||
|       url: 'https://www.reddit.com' | |||
|     }, | |||
|     { | |||
|       className: 'logo-sackgeld', | |||
|       isMask: true, | |||
|       name: 'Sackgeld', | |||
|       title: 'Sackgeld.com – Apps für ein höheres Sackgeld', | |||
|       url: 'https://www.sackgeld.com' | |||
|     }, | |||
|     { | |||
|       className: 'logo-selfh-st', | |||
|       isMask: true, | |||
|       name: 'selfh.st', | |||
|       title: 'selfh.st — Self-hosted content and software', | |||
|       url: 'https://selfh.st' | |||
|     }, | |||
|     { | |||
|       className: 'logo-sourceforge', | |||
|       isMask: true, | |||
|       name: 'SourceForge', | |||
|       title: | |||
|         'SourceForge: The Complete Open-Source and Business Software Platform', | |||
|       url: 'https://sourceforge.net' | |||
|     }, | |||
|     { | |||
|       className: 'logo-umbrel', | |||
|       isMask: true, | |||
|       name: 'Umbrel', | |||
|       title: 'Umbrel — A personal server OS for self-hosting', | |||
|       url: 'https://umbrel.com' | |||
|     }, | |||
|     { | |||
|       className: 'logo-unraid', | |||
|       isMask: true, | |||
|       name: 'Unraid', | |||
|       title: 'Unraid | Unleash Your Hardware', | |||
|       url: 'https://unraid.net' | |||
|     } | |||
|   ]; | |||
| 
 | |||
|   public readonly logosRepeated = [...this.logos, ...this.logos]; | |||
| } | |||
					Loading…
					
					
				
		Reference in new issue