Compare commits
	
		
			2 Commits
		
	
	
		
			678ce12de4
			...
			c1ed021196
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | c1ed021196 | |
|  | efb94f14da | 
							
								
								
									
										69
									
								
								README.md
								
								
								
								
							
							
						
						
									
										69
									
								
								README.md
								
								
								
								
							|  | @ -264,6 +264,75 @@ export class AppComponent { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | ### Card Components | ||||||
|  | 
 | ||||||
|  | **Selectors:** `ui-card`, `ui-card-title`, `ui-card-menu`, `ui-card-footer`   | ||||||
|  | **Description:** Zestaw komponentów do tworzenia kart z opcjonalnym nagłówkiem, menu i stopką. | ||||||
|  | 
 | ||||||
|  | **CardComponent (`ui-card`) Properties:** | ||||||
|  | - `title?: string` - Tytuł karty wyświetlany w nagłówku | ||||||
|  | - `margin: 0.5rem` - Domyślny margin karty | ||||||
|  | - Kolory jak ui-menu (--ui-color-menu-bg, --ui-color-menu-text) | ||||||
|  | 
 | ||||||
|  | **Standalone Components:** | ||||||
|  | - `ui-card-title` - Tytuł karty z własnymi stylami | ||||||
|  | - `ui-card-menu` - Menu/przyciski w nagłówku | ||||||
|  | - `ui-card-footer` - Stopka karty z własnymi stylami | ||||||
|  | 
 | ||||||
|  | **Usage:** | ||||||
|  | ```html | ||||||
|  | <!-- Podstawowe użycie --> | ||||||
|  | <ui-card title="Tytuł karty"> | ||||||
|  |   <p>Zawartość karty</p> | ||||||
|  | </ui-card> | ||||||
|  | 
 | ||||||
|  | <!-- Pełne użycie z wszystkimi slotami --> | ||||||
|  | <ui-card title="Statystyki"> | ||||||
|  |   <ui-card-menu> | ||||||
|  |     <button class="btn btn-sm">Edytuj</button> | ||||||
|  |     <button class="btn btn-sm">Usuń</button> | ||||||
|  |   </ui-card-menu> | ||||||
|  |    | ||||||
|  |   <div class="stats"> | ||||||
|  |     <div class="stat-item"> | ||||||
|  |       <span class="value">123</span> | ||||||
|  |       <span class="label">Użytkownicy</span> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  |    | ||||||
|  |   <ui-card-footer> | ||||||
|  |     <small class="text-muted">Ostatnia aktualizacja: dziś</small> | ||||||
|  |   </ui-card-footer> | ||||||
|  | </ui-card> | ||||||
|  | 
 | ||||||
|  | <!-- Niestandardowy tytuł --> | ||||||
|  | <ui-card> | ||||||
|  |   <ui-card-title> | ||||||
|  |     <div class="d-flex align-items-center"> | ||||||
|  |       <i class="icon-chart"></i> | ||||||
|  |       <span>Raporty sprzedaży</span> | ||||||
|  |     </div> | ||||||
|  |   </ui-card-title> | ||||||
|  |    | ||||||
|  |   <ui-card-menu> | ||||||
|  |     <span class="text-success">+12%</span> | ||||||
|  |   </ui-card-menu> | ||||||
|  |    | ||||||
|  |   <div class="chart-container"> | ||||||
|  |     <!-- wykres --> | ||||||
|  |   </div> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | **CSS Variables:** | ||||||
|  | - `--ui-color-card-bg` - tło karty | ||||||
|  | - `--ui-color-card-border` - kolor obramowania | ||||||
|  | - `--ui-color-card-title` - kolor tytułu | ||||||
|  | - `--ui-color-card-text` - kolor tekstu | ||||||
|  | - `--ui-color-card-footer-bg` - tło stopki | ||||||
|  | - `--ui-shadow-card` - standardowy cień karty | ||||||
|  | - `--ui-shadow-card-hover` - cień przy hover | ||||||
|  | 
 | ||||||
| ## Development | ## Development | ||||||
| 
 | 
 | ||||||
| ### Building | ### Building | ||||||
|  |  | ||||||
|  | @ -4,9 +4,133 @@ | ||||||
|     align-items: center; |     align-items: center; | ||||||
|     justify-content: center; |     justify-content: center; | ||||||
|     padding: 0px 0.75rem; |     padding: 0px 0.75rem; | ||||||
|  |     min-height: 2rem; | ||||||
|  |     border-radius: 4px; | ||||||
|  | 
 | ||||||
|  |     --border-color-rgb: var(--ui-color-background-rgb); | ||||||
|  |     --text-color-rgb: var(--ui-color-text-rgb); | ||||||
|  |     --background-color-rgb: var(--ui-color-background-rgb); | ||||||
|  |     --border-color-alpha: 0; | ||||||
|  |     --background-color-alpha: 0; | ||||||
|  |     --background-hover-rgb: var(--ui-color-content-bg-rgb); | ||||||
|  | 
 | ||||||
|  |     --border-color: rgb(var(--border-color-rgb)); | ||||||
|  |     --text-color: rgb(var(--text-color-rgb)); | ||||||
|  |     --background-color: rgb(var(--background-color-rgb)); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     background: rgba(var(--background-color-rgb), var(--background-color-alpha)); | ||||||
|  |     color: var(--text-color); | ||||||
|  |     border: 1px solid rgba(var(--border-color-rgb), var(--border-color-alpha)); | ||||||
|  | 
 | ||||||
|  |     &[size="small"] { | ||||||
|  |         font-size: 0.85rem; | ||||||
|  |         padding: 0px 0.5rem; | ||||||
|  |         min-height: 1.5rem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[size="large"] { | ||||||
|  |         font-size: 1.2rem; | ||||||
|  |         padding: 0px 1rem; | ||||||
|  |         min-height: 2.5rem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="primary"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-primary-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-primary-hard-rgb); | ||||||
|  |         --text-color-rgb: var(--ui-color-primary-contrast-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-primary-rgb); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="secondary"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-secondary-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-secondary-hard-rgb); | ||||||
|  |         --text-color-rgb: var(--ui-color-secondary-contrast-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-secondary-rgb); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="danger"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-danger-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-danger-hard-rgb); | ||||||
|  |         --text-color-rgb: var(--ui-color-danger-contrast-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-danger-rgb); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="warning"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-warning-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-warning-hard-rgb); | ||||||
|  |         --text-color-rgb: var(--ui-color-warning-contrast-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-warning-rgb); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="info"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-info-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-info-hard-rgb); | ||||||
|  |         --text-color-rgb: var(--ui-color-info-contrast-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-info-rgb); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[theme="success"] { | ||||||
|  |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--ui-color-success-rgb); | ||||||
|  |         --background-hover-rgb: var(--ui-color-success-hard-rgb); | ||||||
|  |         --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         --border-color-rgb: var(--ui-color-success-rgb); | ||||||
|  |         &:not(&[outlined=true]) { | ||||||
|  |             --text-color-rgb: var(--ui-color-success-contrast-rgb); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &[outlined="true"] { | ||||||
|  |         --background-color-alpha: 0; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --text-color-rgb: var(--border-color-rgb); | ||||||
|  |         &[theme="default"] { | ||||||
|  |             --text-color-rgb: var(--ui-color-text-rgb); | ||||||
|  |             --border-color-rgb: var(--ui-color-text-rgb); | ||||||
|  |             --background-hover-rgb: var(--ui-color-text-rgb); | ||||||
|  |             --text-hover-rgb: var(--ui-color-background-rgb); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &.active, | ||||||
|     &.focus, |     &.focus, | ||||||
|     &:active, |     &:active, | ||||||
|     &:hover{ |     &:hover{ | ||||||
|         background: var(--ui-color-button-hover, rgba(255, 255, 255, 0.1)); |         --background-color-alpha: 1; | ||||||
|  |         --border-color-alpha: 1; | ||||||
|  |         --background-color-rgb: var(--background-hover-rgb); | ||||||
|  |         --border-color-rgb: var(--background-hover-rgb); | ||||||
|  |         --text-color-rgb: var(--text-hover-rgb); | ||||||
|  | 
 | ||||||
|  |         &[outlined=true] { | ||||||
|  |             --text-color-rgb: var(--text-hover-rgb); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     & ::ng-deep ~ ui-button{ | ||||||
|  |         margin-left: 0.25rem; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | ::ng-deep ui-header > ui-button{ | ||||||
|  |     border-radius: 0px !important; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| import { Component } from '@angular/core'; | import { Component, HostBinding, Input } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | export type ButtonTheme = 'default' | 'primary' | 'secondary' | 'danger' | 'warning' | 'info' | 'success'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'ui-button', |   selector: 'ui-button', | ||||||
|  | @ -7,5 +9,16 @@ import { Component } from '@angular/core'; | ||||||
|   styleUrl: './button.component.scss' |   styleUrl: './button.component.scss' | ||||||
| }) | }) | ||||||
| export class ButtonComponent { | export class ButtonComponent { | ||||||
|  |     @Input() | ||||||
|  |     @HostBinding('attr.theme') | ||||||
|  |     theme: ButtonTheme = 'default'; | ||||||
|  | 
 | ||||||
|  |     @Input() | ||||||
|  |     @HostBinding('attr.outlined') | ||||||
|  |     outlined: boolean | string = false; | ||||||
|  | 
 | ||||||
|  |     @Input() | ||||||
|  |     @HostBinding('attr.size') | ||||||
|  |     size: 'small' | 'large' | 'default' = 'default'; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,186 @@ | ||||||
|  | # Card Components | ||||||
|  | 
 | ||||||
|  | Zestaw komponentów do tworzenia kart z opcjonalnym nagłówkiem, menu i stopką. | ||||||
|  | 
 | ||||||
|  | ## Komponenty | ||||||
|  | 
 | ||||||
|  | - `ui-card` - główny kontener karty | ||||||
|  | - `ui-card-title` - tytuł karty (standalone komponent) | ||||||
|  | - `ui-card-menu` - menu/przyciski w nagłówku (standalone komponent)   | ||||||
|  | - `ui-card-footer` - stopka karty (standalone komponent) | ||||||
|  | 
 | ||||||
|  | ## Użycie | ||||||
|  | 
 | ||||||
|  | ### Podstawowe użycie z tytułem jako Input | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <ui-card title="Tytuł karty"> | ||||||
|  |   <p>Zawartość karty</p> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Użycie z komponentem ui-card-title | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <ui-card> | ||||||
|  |   <ui-card-title> | ||||||
|  |     <h2>Niestandardowy tytuł</h2> | ||||||
|  |     <span class="badge">Nowy</span> | ||||||
|  |   </ui-card-title> | ||||||
|  |   <p>Zawartość karty</p> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Pełne użycie z wszystkimi komponentami | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <ui-card title="Statystyki"> | ||||||
|  |   <ui-card-menu> | ||||||
|  |     <button class="btn btn-sm">Edytuj</button> | ||||||
|  |     <button class="btn btn-sm">Usuń</button> | ||||||
|  |   </ui-card-menu> | ||||||
|  |    | ||||||
|  |   <div class="stats"> | ||||||
|  |     <div class="stat-item"> | ||||||
|  |       <span class="value">123</span> | ||||||
|  |       <span class="label">Użytkownicy</span> | ||||||
|  |     </div> | ||||||
|  |     <div class="stat-item"> | ||||||
|  |       <span class="value">456</span> | ||||||
|  |       <span class="label">Zamówienia</span> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  |    | ||||||
|  |   <ui-card-footer> | ||||||
|  |     <small class="text-muted">Ostatnia aktualizacja: dziś</small> | ||||||
|  |   </ui-card-footer> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Użycie z komponentami ui-card-title i ui-card-menu | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <ui-card> | ||||||
|  |   <ui-card-title> | ||||||
|  |     <div class="d-flex align-items-center"> | ||||||
|  |       <i class="icon-chart"></i> | ||||||
|  |       <span>Raporty sprzedaży</span> | ||||||
|  |     </div> | ||||||
|  |   </ui-card-title> | ||||||
|  |    | ||||||
|  |   <ui-card-menu> | ||||||
|  |     <div class="stats-mini"> | ||||||
|  |       <span class="text-success">+12%</span> | ||||||
|  |     </div> | ||||||
|  |     <button class="btn btn-outline-primary btn-sm">Szczegóły</button> | ||||||
|  |   </ui-card-menu> | ||||||
|  |    | ||||||
|  |   <div class="chart-container"> | ||||||
|  |     <!-- wykres --> | ||||||
|  |   </div> | ||||||
|  |    | ||||||
|  |   <ui-card-footer> | ||||||
|  |     <div class="d-flex justify-content-between"> | ||||||
|  |       <span>Okres: ostatnie 30 dni</span> | ||||||
|  |       <a href="#" class="text-primary">Zobacz więcej</a> | ||||||
|  |     </div> | ||||||
|  |   </ui-card-footer> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## API | ||||||
|  | 
 | ||||||
|  | ### CardComponent (`ui-card`) | ||||||
|  | 
 | ||||||
|  | **Inputs:** | ||||||
|  | | Właściwość | Typ | Domyślna | Opis | | ||||||
|  | |------------|-----|----------|------| | ||||||
|  | | `title` | `string` | `undefined` | Tytuł karty wyświetlany w nagłówku | | ||||||
|  | 
 | ||||||
|  | **Content Projection:** | ||||||
|  | | Selektor | Opis | | ||||||
|  | |----------|------| | ||||||
|  | | `ui-card-title` | Komponent tytułu karty | | ||||||
|  | | `ui-card-menu` | Komponent menu w nagłówku | | ||||||
|  | | (default) | Główna zawartość karty | | ||||||
|  | | `ui-card-footer` | Komponent stopki karty | | ||||||
|  | 
 | ||||||
|  | ### CardTitleComponent (`ui-card-title`) | ||||||
|  | 
 | ||||||
|  | **Opis:** Standalone komponent do wyświetlania tytułu karty z własnymi stylami. | ||||||
|  | 
 | ||||||
|  | ### CardMenuComponent (`ui-card-menu`) | ||||||
|  | 
 | ||||||
|  | **Opis:** Standalone komponent do wyświetlania menu/przycisków w nagłówku karty. | ||||||
|  | 
 | ||||||
|  | ### CardFooterComponent (`ui-card-footer`) | ||||||
|  | 
 | ||||||
|  | **Opis:** Standalone komponent do wyświetlania stopki karty z własnymi stylami. | ||||||
|  | 
 | ||||||
|  | ## Zmienne CSS | ||||||
|  | 
 | ||||||
|  | Komponent używa następujących zmiennych CSS z systemu motywów: | ||||||
|  | 
 | ||||||
|  | ### Kolory | ||||||
|  | - `--ui-color-card-bg` - tło karty | ||||||
|  | - `--ui-color-card-border` - kolor obramowania | ||||||
|  | - `--ui-color-card-title` - kolor tytułu | ||||||
|  | - `--ui-color-card-text` - kolor tekstu | ||||||
|  | - `--ui-color-card-footer-bg` - tło stopki | ||||||
|  | 
 | ||||||
|  | ### Spacing | ||||||
|  | - `--ui-spacing-xs` (4px) - małe odstępy | ||||||
|  | - `--ui-spacing-sm` (8px) - średnie odstępy   | ||||||
|  | - `--ui-spacing-md` (16px) - standardowe padding | ||||||
|  | - `--ui-spacing-lg` (24px) - duże odstępy | ||||||
|  | 
 | ||||||
|  | ### Layout | ||||||
|  | - `--ui-border-radius` (8px) - zaokrąglenie rogów | ||||||
|  | - `--ui-font-size-lg` (18px) - rozmiar czcionki tytułu | ||||||
|  | - `--ui-font-weight-semibold` (600) - grubość czcionki tytułu | ||||||
|  | - `--ui-card-header-min-height` (40px) - minimalna wysokość nagłówka | ||||||
|  | 
 | ||||||
|  | ### Cienie | ||||||
|  | - `--ui-shadow-card` - standardowy cień karty | ||||||
|  | - `--ui-shadow-card-hover` - cień przy hover | ||||||
|  | 
 | ||||||
|  | ## Responsywność | ||||||
|  | 
 | ||||||
|  | Na urządzeniach mobilnych (max-width: 768px): | ||||||
|  | - Nagłówek karty zmienia się na układ kolumnowy | ||||||
|  | - Menu wyrównuje się do prawej strony | ||||||
|  | - Zachowane są wszystkie funkcjonalności | ||||||
|  | 
 | ||||||
|  | ## Przykłady stylizacji | ||||||
|  | 
 | ||||||
|  | ### Karta z kolorowym nagłówkiem | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | .custom-card { | ||||||
|  |   .ui-card-header { | ||||||
|  |     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||||||
|  |     color: white; | ||||||
|  |     margin: -16px -16px 0; | ||||||
|  |     padding: 16px; | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .ui-card-title { | ||||||
|  |     color: white; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Karta z ikoną | ||||||
|  | 
 | ||||||
|  | ```html | ||||||
|  | <ui-card> | ||||||
|  |   <ui-card-title> | ||||||
|  |     <div class="d-flex align-items-center gap-2"> | ||||||
|  |       <i class="icon-settings"></i> | ||||||
|  |       <span>Ustawienia</span> | ||||||
|  |     </div> | ||||||
|  |   </ui-card-title> | ||||||
|  |    | ||||||
|  |   <!-- zawartość --> | ||||||
|  | </ui-card> | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | :host { | ||||||
|  |     padding: 0.5rem; | ||||||
|  |     display: flex; | ||||||
|  |     &:empty{ | ||||||
|  |         display: none; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | import { Component } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'ui-card-footer', | ||||||
|  |   standalone: true, | ||||||
|  |   imports: [], | ||||||
|  |   template: '<ng-content />', | ||||||
|  |   styleUrl: './card-footer.component.scss' | ||||||
|  | }) | ||||||
|  | export class CardFooterComponent { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,7 @@ | ||||||
|  | :host { | ||||||
|  |   display: flex; | ||||||
|  |   gap: var(--ui-spacing-sm, 8px); | ||||||
|  |   align-items: flex-start; | ||||||
|  |   flex-shrink: 0; | ||||||
|  |   padding: 1rem; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | import { Component } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'ui-card-menu', | ||||||
|  |   standalone: true, | ||||||
|  |   imports: [], | ||||||
|  |   template: '<ng-content />', | ||||||
|  |   styleUrl: './card-menu.component.scss' | ||||||
|  | }) | ||||||
|  | export class CardMenuComponent { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | :host { | ||||||
|  |   display: block; | ||||||
|  |   margin: 0; | ||||||
|  |   font-size: var(--ui-font-size-lg, 18px); | ||||||
|  |   font-weight: var(--ui-font-weight-semibold, 600); | ||||||
|  |   color: var(--ui-color-menu-text, #ffffff); | ||||||
|  |   line-height: 1.2; | ||||||
|  |   padding: 1rem; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | import { Component } from '@angular/core'; | ||||||
|  | 
 | ||||||
|  | @Component({ | ||||||
|  |   selector: 'ui-card-title', | ||||||
|  |   standalone: true, | ||||||
|  |   imports: [], | ||||||
|  |   template: `<ng-content />`, | ||||||
|  |   styleUrl: './card-title.component.scss' | ||||||
|  | }) | ||||||
|  | export class CardTitleComponent { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -1 +1,15 @@ | ||||||
| <p>card works!</p> | <div class="header"> | ||||||
|  |     @if (title) { | ||||||
|  |         <div class="title">{{ title }}</div> | ||||||
|  |     } @else { | ||||||
|  |         <ng-content select="ui-card-title"></ng-content> | ||||||
|  |     } | ||||||
|  |     <ng-content select="ui-card-menu"></ng-content> | ||||||
|  | 
 | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <div class="content"> | ||||||
|  |   <ng-content></ng-content> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | <ng-content select="ui-card-footer"></ng-content> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | :host { | ||||||
|  |   display: block; | ||||||
|  |   margin: var(--ui-spacing-sm, 0.5rem); | ||||||
|  |   background: var(--ui-color-menu-bg, #343a40); | ||||||
|  |   border: 1px solid var(--ui-color-menu-border, #d0d0d0); | ||||||
|  |   border-radius: var(--ui-border-radius, 8px); | ||||||
|  |   box-shadow: var(--ui-shadow-card, 0 2px 4px rgba(0, 0, 0, 0.1)); | ||||||
|  |   overflow: hidden; | ||||||
|  |   transition: box-shadow 0.2s ease; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .content { | ||||||
|  |   padding: var(--ui-spacing-md, 16px); | ||||||
|  |   color: var(--ui-color-menu-text, #ffffff); | ||||||
|  |   line-height: 1.5; | ||||||
|  | } | ||||||
|  | .header{ | ||||||
|  |     display: flex; | ||||||
|  |     .title, | ||||||
|  |     ::ng-deep ui-card-title{ | ||||||
|  |         flex-grow: 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,11 +1,13 @@ | ||||||
| import { Component } from '@angular/core'; | import { Component, Input } from '@angular/core'; | ||||||
|  | import { CommonModule } from '@angular/common'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'ui-card', |   selector: 'ui-card', | ||||||
|   imports: [], |   standalone: true, | ||||||
|  |   imports: [CommonModule], | ||||||
|   templateUrl: './card.component.html', |   templateUrl: './card.component.html', | ||||||
|   styleUrl: './card.component.scss' |   styleUrl: './card.component.scss' | ||||||
| }) | }) | ||||||
| export class CardComponent { | export class CardComponent { | ||||||
| 
 |   @Input() title?: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1 +1,18 @@ | ||||||
| <p>input works!</p> | <div  (click)="stopPropagation($event)"> | ||||||
|  |     <ng-content select="[place=before]"></ng-content> | ||||||
|  | </div> | ||||||
|  | <input | ||||||
|  |     #input | ||||||
|  |     [type]="type" | ||||||
|  |     [value]="value" | ||||||
|  |     [disabled]="disabled" | ||||||
|  |     [required]="required" | ||||||
|  |     (input)="onInputChange($event)" | ||||||
|  |     (focus)="onInputFocus()" | ||||||
|  |     (blur)="onInputBlur()" | ||||||
|  |     class="ui-input-field" | ||||||
|  | /> | ||||||
|  | <label class="ui-input-label" [class.floating]="isFocused || filled">{{ placeholder }}</label> | ||||||
|  | <div (click)="stopPropagation($event)"> | ||||||
|  |     <ng-content select="[place=after]"></ng-content> | ||||||
|  | </div> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,94 @@ | ||||||
|  | :host { | ||||||
|  |     display: block; | ||||||
|  |     width: 100%; | ||||||
|  | 
 | ||||||
|  |     --background-color-rgb: var(--ui-color-input-bg-rgb, 255, 255, 255); | ||||||
|  |     --accent-color-rgb: var(--ui-color-input-label-focus-rgb, 0, 123, 255); | ||||||
|  |     --text-color-rgb: var(--ui-color-input-text-rgb, 33, 37, 41); | ||||||
|  | 
 | ||||||
|  |     --background-color: var(--ui-color-input-bg, rgb(var(--background-color-rgb))); | ||||||
|  |     --accent-color: var(--ui-color-input-label-focus, rgb(var(--accent-color-rgb))); | ||||||
|  |     --text-color: var(--ui-color-input-text, rgb(var(--text-color-rgb))); | ||||||
|  | 
 | ||||||
|  |     background-color: var(--background-color); | ||||||
|  |     box-shadow: 0px 0px 0px 0px var(--background-color), 0px 0px 0px 0px var(--accent-color); | ||||||
|  |     transition: all 0.2s ease-in-out; | ||||||
|  | 
 | ||||||
|  |     position: relative; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: stretch; | ||||||
|  |     gap: 0.5rem; | ||||||
|  |     cursor: text; | ||||||
|  |     min-height: 2rem; | ||||||
|  |     &.invalid{ | ||||||
|  |         --accent-color: var(--ui-color-input-label-invalid, #dc3545); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &.active:not(.disabled), | ||||||
|  |     &.focused{ | ||||||
|  |         box-shadow: 0px 0px 0px 9px var(--background-color), 0px 0px 0px 10px var(--accent-color); | ||||||
|  |     } | ||||||
|  |     &.hovered { | ||||||
|  |         box-shadow: 0px 0px 0px 0px var(--background-color), 0px 0px 0px 1px var(--accent-color); | ||||||
|  |     } | ||||||
|  |     &.active:not(.disabled), | ||||||
|  |     &.focused, | ||||||
|  |     &.hovered { | ||||||
|  |         --background-color: var(--ui-color-input-bg-hover, #313b44); | ||||||
|  |     } | ||||||
|  |     &.disabled { | ||||||
|  |         opacity: 0.6; | ||||||
|  |         cursor: not-allowed; | ||||||
|  |     } | ||||||
|  |     .ui-input-label { | ||||||
|  |         position: absolute; | ||||||
|  |         left: 0.5rem; | ||||||
|  |         top: 50%; | ||||||
|  |         transform: translateY(-50%); | ||||||
|  |         font-size: 1rem; | ||||||
|  |         color: rgba(var(--accent-color-rgb), 0.4); | ||||||
|  |         pointer-events: none; | ||||||
|  |         transition: all 0.2s ease-in-out; | ||||||
|  |         background-color: transparent; | ||||||
|  |         border-radius: 6px; | ||||||
|  |         padding: 0 0.25rem; | ||||||
|  |         white-space: nowrap; | ||||||
|  | 
 | ||||||
|  |         &.floating { | ||||||
|  |             top: 0; | ||||||
|  |             color: var(--accent-color); | ||||||
|  |             transform: translateY(-50%); | ||||||
|  |             font-size: 0.7rem; | ||||||
|  |             background-color: var(--background-color); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ::ng-deep { | ||||||
|  |         > div { | ||||||
|  |             display: flex; | ||||||
|  |             > [place] { | ||||||
|  |                 display: flex; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .ui-input-field { | ||||||
|  |     flex: 1; | ||||||
|  |     padding: 0.5rem 0.25rem; | ||||||
|  |     border: none; | ||||||
|  |     outline: none; | ||||||
|  |     background: transparent; | ||||||
|  |     font-size: 1rem; | ||||||
|  |     line-height: 1.5; | ||||||
|  |     color: var(--ui-color-input-text, #212529); | ||||||
|  | 
 | ||||||
|  |     &::placeholder { | ||||||
|  |         color: transparent; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     &:disabled { | ||||||
|  |         cursor: not-allowed; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -1,23 +0,0 @@ | ||||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; |  | ||||||
| 
 |  | ||||||
| import { InputComponent } from './input.component'; |  | ||||||
| 
 |  | ||||||
| describe('InputComponent', () => { |  | ||||||
|   let component: InputComponent; |  | ||||||
|   let fixture: ComponentFixture<InputComponent>; |  | ||||||
| 
 |  | ||||||
|   beforeEach(async () => { |  | ||||||
|     await TestBed.configureTestingModule({ |  | ||||||
|       imports: [InputComponent] |  | ||||||
|     }) |  | ||||||
|     .compileComponents(); |  | ||||||
| 
 |  | ||||||
|     fixture = TestBed.createComponent(InputComponent); |  | ||||||
|     component = fixture.componentInstance; |  | ||||||
|     fixture.detectChanges(); |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   it('should create', () => { |  | ||||||
|     expect(component).toBeTruthy(); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  | @ -1,11 +1,134 @@ | ||||||
| import { Component } from '@angular/core'; | import { Component, Input, HostBinding, ElementRef, forwardRef, HostListener } from '@angular/core'; | ||||||
|  | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||||||
|  | import { CommonModule } from '@angular/common'; | ||||||
|  | import { FormModule } from '../../form-module'; | ||||||
| 
 | 
 | ||||||
| @Component({ | @Component({ | ||||||
|   selector: 'ui-input', |   selector: 'ui-input', | ||||||
|   imports: [], |   exportAs: 'uiInput', | ||||||
|  |   standalone: true, | ||||||
|  |   imports: [CommonModule], | ||||||
|   templateUrl: './input.component.html', |   templateUrl: './input.component.html', | ||||||
|   styleUrl: './input.component.scss' |   styleUrl: './input.component.scss', | ||||||
|  |   providers: [ | ||||||
|  |     { | ||||||
|  |       provide: NG_VALUE_ACCESSOR, | ||||||
|  |       useExisting: forwardRef(() => InputComponent), | ||||||
|  |       multi: true | ||||||
|  |     } | ||||||
|  |   ] | ||||||
| }) | }) | ||||||
| export class InputComponent { | export class InputComponent implements ControlValueAccessor { | ||||||
|  |   @Input() | ||||||
|  |   placeholder: string = ''; | ||||||
| 
 | 
 | ||||||
|  |   @Input() | ||||||
|  |   type: string = 'text'; | ||||||
|  | 
 | ||||||
|  |   @Input() | ||||||
|  |   @HostBinding('class.disabled') | ||||||
|  |   disabled: boolean = false; | ||||||
|  | 
 | ||||||
|  |   @Input() | ||||||
|  |   required: boolean = false; | ||||||
|  | 
 | ||||||
|  |   value: string = ''; | ||||||
|  | 
 | ||||||
|  |   @HostBinding('class.focused') | ||||||
|  |   isFocused: boolean = false; | ||||||
|  | 
 | ||||||
|  |   @HostBinding('class.hovered') | ||||||
|  |   isHovered: boolean = false; | ||||||
|  | 
 | ||||||
|  |   isTouched: boolean = false; | ||||||
|  | 
 | ||||||
|  |   @HostBinding('class.active') | ||||||
|  |   isActive: boolean = false; | ||||||
|  | 
 | ||||||
|  |   private onChange = (value: string) => {}; | ||||||
|  |   private onTouched = () => {}; | ||||||
|  | 
 | ||||||
|  |   @HostBinding('class.filled') | ||||||
|  |   get filled() { | ||||||
|  |     return this.value && this.value.length > 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @HostBinding('class.invalid') | ||||||
|  |   get invalid() { | ||||||
|  |     return this.required && this.isTouched && (!this.value || this.value.length === 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   constructor(private elementRef: ElementRef) {} | ||||||
|  | 
 | ||||||
|  |   public stopPropagation(event: Event) { | ||||||
|  |     event.stopPropagation(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public focus() { | ||||||
|  |     console.log({elementRef: this.elementRef.nativeElement}); | ||||||
|  |     this.elementRef.nativeElement.querySelector('input')?.focus(); | ||||||
|  |     this.onInputFocus(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public blur() { | ||||||
|  |     this.elementRef.nativeElement.querySelector('input')?.blur(); | ||||||
|  |     this.onInputBlur(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @HostListener('mouseenter') | ||||||
|  |   public hover() { | ||||||
|  |     this.isHovered = true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @HostListener('mouseleave') | ||||||
|  |   public leave() { | ||||||
|  |     this.isHovered = false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onInputChange(event: Event): void { | ||||||
|  |     const target = event.target as HTMLInputElement; | ||||||
|  |     this.value = target.value; | ||||||
|  |     this.onChange(this.value); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onInputFocus(): void { | ||||||
|  |     this.isFocused = true; | ||||||
|  |     this.isActive = true; | ||||||
|  |     FormModule.ActiveInput = this; | ||||||
|  |     (window as any).KeyEventSafeModeEnabled = true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   onInputBlur(): void { | ||||||
|  |     this.isFocused = false; | ||||||
|  |     this.isActive = false; | ||||||
|  |     this.isTouched = true; | ||||||
|  |     this.onTouched(); | ||||||
|  |     FormModule.ActiveInput = undefined; | ||||||
|  |     (window as any).KeyEventSafeModeEnabled = undefined; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @HostListener('click') | ||||||
|  |   onContainerClick(): void { | ||||||
|  |     const input = this.elementRef.nativeElement.querySelector('input'); | ||||||
|  |     if (input && !this.disabled) { | ||||||
|  |       this.focus(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // ControlValueAccessor implementation
 | ||||||
|  |   writeValue(value: string): void { | ||||||
|  |     this.value = value || ''; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   registerOnChange(fn: (value: string) => void): void { | ||||||
|  |     this.onChange = fn; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   registerOnTouched(fn: () => void): void { | ||||||
|  |     this.onTouched = fn; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setDisabledState(isDisabled: boolean): void { | ||||||
|  |     this.disabled = isDisabled; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,12 +1,17 @@ | ||||||
| import { NgModule } from '@angular/core'; | import { NgModule } from '@angular/core'; | ||||||
| import { CommonModule } from '@angular/common'; | import { CommonModule } from '@angular/common'; | ||||||
| 
 | import { InputComponent } from './components/input/input.component'; | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   declarations: [], |   declarations: [], | ||||||
|   imports: [ |   imports: [ | ||||||
|     CommonModule |     CommonModule, | ||||||
|  |     InputComponent | ||||||
|  |   ], | ||||||
|  |   exports: [ | ||||||
|  |     InputComponent | ||||||
|   ] |   ] | ||||||
| }) | }) | ||||||
| export class FormModule { } | export class FormModule { | ||||||
|  |     public static ActiveInput?: InputComponent; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,141 @@ | ||||||
|  | # UI Contrast Color Function | ||||||
|  | 
 | ||||||
|  | ## Opis | ||||||
|  | 
 | ||||||
|  | Funkcja `ui-contrast-color()` automatycznie wybiera najlepszy kolor kontrastujący na podstawie jasności koloru bazowego. Jest to narzędzie do zapewnienia optymalnej czytelności tekstu na różnych tłach. | ||||||
|  | 
 | ||||||
|  | ## Składnia | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | @function ui-contrast-color($base-color, $light-color, $dark-color) | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Parametry | ||||||
|  | 
 | ||||||
|  | - `$base-color` - Kolor bazowy, dla którego sprawdzana jest jasność | ||||||
|  | - `$light-color` - Kolor zwracany dla ciemnych kolorów bazowych (zwykle kolor tekstu w jasnym motywie lub tła w ciemnym motywie) | ||||||
|  | - `$dark-color` - Kolor zwracany dla jasnych kolorów bazowych (zwykle kolor tła w jasnym motywie lub tekstu w ciemnym motywie) | ||||||
|  | 
 | ||||||
|  | ### Zwracana wartość | ||||||
|  | 
 | ||||||
|  | Funkcja zwraca: | ||||||
|  | - `$dark-color` jeśli jasność `$base-color` > 50% (kolor bliżej białego) | ||||||
|  | - `$light-color` jeśli jasność `$base-color` ≤ 50% (kolor bliżej czarnego) | ||||||
|  | 
 | ||||||
|  | ## Mechanizm działania | ||||||
|  | 
 | ||||||
|  | 1. Funkcja oblicza jasność koloru bazowego używając `color.channel($base-color, "lightness", $space: hsl)` | ||||||
|  | 2. Porównuje jasność z progiem 50% | ||||||
|  | 3. Zwraca odpowiedni kolor kontrastujący | ||||||
|  | 
 | ||||||
|  | ## Przykłady użycia | ||||||
|  | 
 | ||||||
|  | ### Podstawowe użycie | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | @use 'theme-mixins' as theme; | ||||||
|  | 
 | ||||||
|  | .button { | ||||||
|  |   $bg: #007bff; // Niebieski | ||||||
|  |   background-color: $bg; | ||||||
|  |   color: theme.ui-contrast-color($bg, #ffffff, #333333); | ||||||
|  |   // Wynik: biały tekst, bo niebieski jest ciemny | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Kolory statusów | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | .status-badge { | ||||||
|  |   &.success { | ||||||
|  |     $color: #28a745; // Zielony | ||||||
|  |     background-color: $color; | ||||||
|  |     color: theme.ui-contrast-color($color, white, black); | ||||||
|  |     // Wynik: biały tekst | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   &.warning { | ||||||
|  |     $color: #ffc107; // Żółty | ||||||
|  |     background-color: $color; | ||||||
|  |     color: theme.ui-contrast-color($color, white, black); | ||||||
|  |     // Wynik: czarny tekst (żółty jest jasny) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Użycie z motywami | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | // Dla jasnego motywu | ||||||
|  | .component.light-theme { | ||||||
|  |   $bg: var(--ui-color-primary); | ||||||
|  |   $primary: #2c3e50; // Wartość z motywu | ||||||
|  |    | ||||||
|  |   background-color: $primary; | ||||||
|  |   color: theme.ui-contrast-color($primary, #ffffff, #333333); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Integracja z systemem motywów | ||||||
|  | 
 | ||||||
|  | Funkcja jest już zintegrowana z systemem motywów biblioteki UI i automatycznie generuje zmienne contrast: | ||||||
|  | 
 | ||||||
|  | ### Zmienne HEX | ||||||
|  | - `--ui-color-primary-contrast` | ||||||
|  | - `--ui-color-secondary-contrast` | ||||||
|  | - `--ui-color-success-contrast` | ||||||
|  | - `--ui-color-warning-contrast` | ||||||
|  | - `--ui-color-danger-contrast` | ||||||
|  | - `--ui-color-info-contrast` | ||||||
|  | 
 | ||||||
|  | ### Zmienne RGB | ||||||
|  | - `--ui-color-primary-contrast-rgb` | ||||||
|  | - `--ui-color-secondary-contrast-rgb` | ||||||
|  | - `--ui-color-success-contrast-rgb` | ||||||
|  | - `--ui-color-warning-contrast-rgb` | ||||||
|  | - `--ui-color-danger-contrast-rgb` | ||||||
|  | - `--ui-color-info-contrast-rgb` | ||||||
|  | 
 | ||||||
|  | ## Użycie zmiennych CSS | ||||||
|  | 
 | ||||||
|  | ```scss | ||||||
|  | .dynamic-component { | ||||||
|  |   background-color: var(--ui-color-primary); | ||||||
|  |   color: var(--ui-color-primary-contrast); | ||||||
|  |    | ||||||
|  |   // Z przezroczystością | ||||||
|  |   background-color: rgba(var(--ui-color-primary-rgb), 0.8); | ||||||
|  |   color: var(--ui-color-primary-contrast); | ||||||
|  |    | ||||||
|  |   // Obramowanie z przezroczystym kontrastem | ||||||
|  |   border: 1px solid rgba(var(--ui-color-primary-contrast-rgb), 0.3); | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Zalety | ||||||
|  | 
 | ||||||
|  | 1. **Automatyczny dobór** - Nie musisz ręcznie sprawdzać, czy kolor jest jasny czy ciemny | ||||||
|  | 2. **Spójność** - Wszystkie komponenty używają tej samej logiki kontrastowania | ||||||
|  | 3. **Dostępność** - Zapewnia lepszą czytelność dla użytkowników | ||||||
|  | 4. **Elastyczność** - Możesz definiować własne kolory jasne i ciemne dla różnych kontekstów | ||||||
|  | 5. **Integracja z motywami** - Automatycznie generuje zmienne CSS dla wszystkich motywów | ||||||
|  | 
 | ||||||
|  | ## Uwagi techniczne | ||||||
|  | 
 | ||||||
|  | - Funkcja działa w czasie kompilacji SCSS z konkretnymi wartościami kolorów | ||||||
|  | - Dla dynamicznych kolorów CSS używaj pre-wygenerowanych zmiennych `*-contrast` | ||||||
|  | - Próg 50% jasności jest optymalny dla większości przypadków użycia | ||||||
|  | - Używa nowoczesnej składni Sass (`color.channel()`) bez ostrzeżeń o deprecacji | ||||||
|  | 
 | ||||||
|  | ## Przykłady kolorów i wyników | ||||||
|  | 
 | ||||||
|  | | Kolor bazowy | Nazwa | Jasność | Wynik dla (white, black) | | ||||||
|  | |--------------|-------|---------|--------------------------| | ||||||
|  | | `#ffffff` | Biały | 100% | `black` | | ||||||
|  | | `#ffc107` | Żółty | ~75% | `black` | | ||||||
|  | | `#28a745` | Zielony | ~45% | `white` | | ||||||
|  | | `#007bff` | Niebieski | ~35% | `white` | | ||||||
|  | | `#dc3545` | Czerwony | ~30% | `white` | | ||||||
|  | | `#000000` | Czarny | 0% | `white` | | ||||||
|  | 
 | ||||||
|  | Funkcja zapewnia optymalny kontrast dla wszystkich kolorów w systemie motywów biblioteki UI. | ||||||
|  | @ -1,12 +1,29 @@ | ||||||
| /* Theme Integration Mixins for UI Library */ | /* Theme Integration Mixins for UI Library */ | ||||||
| 
 | 
 | ||||||
|  | @use 'sass:color'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Mixin that returns the best contrasting color based on lightness | ||||||
|  |  * If the base color is closer to white than black, returns the dark color | ||||||
|  |  * If the base color is closer to black than white, returns the light color | ||||||
|  |  * | ||||||
|  |  * @param $base-color - The color to check lightness for | ||||||
|  |  * @param $light-color - Color to return for dark base colors (usually text color in light theme or background in dark theme) | ||||||
|  |  * @param $dark-color - Color to return for light base colors (usually background color in light theme or text color in dark theme) | ||||||
|  |  * @return Color - The contrasting color | ||||||
|  |  */ | ||||||
|  | @function ui-contrast-color($base-color, $light-color, $dark-color) { | ||||||
|  |   $lightness: color.channel($base-color, "lightness", $space: hsl); | ||||||
|  |   @return if($lightness > 40%, $dark-color, $light-color); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * Mixin that generates CSS variables for UI library components |  * Mixin that generates CSS variables for UI library components | ||||||
|  * This mixin should be called by the main application to provide colors for the UI library |  * This mixin should be called by the main application to provide colors for the UI library | ||||||
|  * The UI library uses only --ui-* variables with hardcoded fallbacks for portability |  * The UI library uses only --ui-* variables with hardcoded fallbacks for portability | ||||||
|  *  |  * | ||||||
|  * @param $primary - Primary color for the theme |  * @param $primary - Primary color for the theme | ||||||
|  * @param $secondary - Secondary color for the theme   |  * @param $secondary - Secondary color for the theme | ||||||
|  * @param $background - Background color for the theme |  * @param $background - Background color for the theme | ||||||
|  * @param $text - Text color for the theme |  * @param $text - Text color for the theme | ||||||
|  * @param $border - Border color for the theme |  * @param $border - Border color for the theme | ||||||
|  | @ -18,29 +35,181 @@ | ||||||
|   $background: #343a40, |   $background: #343a40, | ||||||
|   $text: #ffffff, |   $text: #ffffff, | ||||||
|   $border: #d0d0d0, |   $border: #d0d0d0, | ||||||
|   $hover: rgba(255, 255, 255, 0.1) |   $hover: rgba(255, 255, 255, 0.1), | ||||||
|  |   $success: #28a745, | ||||||
|  |   $warning: #ffc107, | ||||||
|  |   $danger: #dc3545, | ||||||
|  |   $info: #17a2b8 | ||||||
| ) { | ) { | ||||||
|   /* UI Library specific CSS variables - only these should be used in UI components */ |   /* Assign SCSS variables for each color */ | ||||||
|   --ui-color-primary: #{$primary}; |   $primary-color: $primary; | ||||||
|   --ui-color-secondary: #{$secondary}; |   $secondary-color: $secondary; | ||||||
|   --ui-color-background: #{$background}; |   $background-color: $background; | ||||||
|   --ui-color-text: #{$text}; |   $text-color: $text; | ||||||
|   --ui-color-border: #{$border}; |   $border-color: $border; | ||||||
|   --ui-color-hover: #{$hover}; |   $hover-color: $hover; | ||||||
|    |   $success-color: $success; | ||||||
|   /* Derived colors for specific UI components */ |   $warning-color: $warning; | ||||||
|   --ui-color-menu-bg: #{$background}; |   $danger-color: $danger; | ||||||
|   --ui-color-menu-text: #{$text}; |   $info-color: $info; | ||||||
|   --ui-color-menu-hover: #{$hover}; | 
 | ||||||
|   --ui-color-menu-active: color-mix(in srgb, #{$background} 80%, black 20%); |   /* Derived colors as SCSS variables using SCSS functions */ | ||||||
|   --ui-color-menu-border: #{$border}; |   $menu-active-color: color.mix(black, $background-color, 20%); | ||||||
|    |   $content-bg-color: color.mix(white, $background-color, 5%); | ||||||
|   --ui-color-content-bg: color-mix(in srgb, #{$background} 95%, white 5%); |   $icon-muted-color: rgba($text-color, 0.6); | ||||||
|   --ui-color-content-text: #{$text}; |   $card-bg-color: if($background-color == #343a40, #ffffff, color.mix(white, $background-color, 5%)); | ||||||
|    |   $card-text-color: if($text-color == #ffffff, #333333, $text-color); | ||||||
|   --ui-color-icon-primary: #{$primary}; |   $card-footer-bg-color: if($background-color == #343a40, #f8f9fa, color.mix(white, $background-color, 10%)); | ||||||
|   --ui-color-icon-secondary: #{$secondary}; | 
 | ||||||
|   --ui-color-icon-muted: color-mix(in srgb, #{$text} 60%, transparent 40%); |   /* UI Library specific CSS variables - HEX format */ | ||||||
|  |   --ui-color-primary: #{$primary-color}; | ||||||
|  |   --ui-color-secondary: #{$secondary-color}; | ||||||
|  |   --ui-color-background: #{$background-color}; | ||||||
|  |   --ui-color-text: #{$text-color}; | ||||||
|  |   --ui-color-border: #{$border-color}; | ||||||
|  |   --ui-color-hover: #{$hover-color}; | ||||||
|  | 
 | ||||||
|  |   /* Derived colors for specific UI components - HEX format */ | ||||||
|  |   --ui-color-menu-bg: #{$background-color}; | ||||||
|  |   --ui-color-menu-text: #{$text-color}; | ||||||
|  |   --ui-color-menu-hover: #{$hover-color}; | ||||||
|  |   --ui-color-menu-active: #{$menu-active-color}; | ||||||
|  |   --ui-color-menu-border: #{$border-color}; | ||||||
|  | 
 | ||||||
|  |   --ui-color-content-bg: #{$content-bg-color}; | ||||||
|  |   --ui-color-content-text: #{$text-color}; | ||||||
|  | 
 | ||||||
|  |   --ui-color-icon-primary: #{$primary-color}; | ||||||
|  |   --ui-color-icon-secondary: #{$secondary-color}; | ||||||
|  |   --ui-color-icon-muted: #{$icon-muted-color}; | ||||||
|  | 
 | ||||||
|  |   /* Card component variables - HEX format */ | ||||||
|  |   --ui-color-card-bg: #{$card-bg-color}; | ||||||
|  |   --ui-color-card-border: #{$border-color}; | ||||||
|  |   --ui-color-card-title: #{$primary-color}; | ||||||
|  |   --ui-color-card-text: #{$card-text-color}; | ||||||
|  |   --ui-color-card-footer-bg: #{$card-footer-bg-color}; | ||||||
|  | 
 | ||||||
|  |   /* Status colors - HEX format */ | ||||||
|  |   --ui-color-success: #{$success-color}; | ||||||
|  |   --ui-color-warning: #{$warning-color}; | ||||||
|  |   --ui-color-danger: #{$danger-color}; | ||||||
|  |   --ui-color-info: #{$info-color}; | ||||||
|  | 
 | ||||||
|  |   /* Contrast colors - automatically choose text or background color for best contrast */ | ||||||
|  |   --ui-color-primary-contrast: #{ui-contrast-color($primary-color, $text-color, $background-color)}; | ||||||
|  |   --ui-color-secondary-contrast: #{ui-contrast-color($secondary-color, $text-color, $background-color)}; | ||||||
|  |   --ui-color-success-contrast: #{ui-contrast-color($success-color, $text-color, $background-color)}; | ||||||
|  |   --ui-color-warning-contrast: #{ui-contrast-color($warning-color, $text-color, $background-color)}; | ||||||
|  |   --ui-color-danger-contrast: #{ui-contrast-color($danger-color, $text-color, $background-color)}; | ||||||
|  |   --ui-color-info-contrast: #{ui-contrast-color($info-color, $text-color, $background-color)}; | ||||||
|  | 
 | ||||||
|  |   /* Hard colors - darker in light themes, lighter in dark themes */ | ||||||
|  |   --ui-color-primary-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%))}; | ||||||
|  |   --ui-color-secondary-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%))}; | ||||||
|  |   --ui-color-success-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%))}; | ||||||
|  |   --ui-color-warning-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%))}; | ||||||
|  |   --ui-color-danger-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%))}; | ||||||
|  |   --ui-color-info-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%))}; | ||||||
|  | 
 | ||||||
|  |   /* Soft colors - lighter in light themes, darker in dark themes */ | ||||||
|  |   --ui-color-primary-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%))}; | ||||||
|  |   --ui-color-secondary-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%))}; | ||||||
|  |   --ui-color-success-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%))}; | ||||||
|  |   --ui-color-warning-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%))}; | ||||||
|  |   --ui-color-danger-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%))}; | ||||||
|  |   --ui-color-info-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%))}; | ||||||
|  | 
 | ||||||
|  |   /* RGB versions for rgba() usage - Basic colors */ | ||||||
|  |   --ui-color-primary-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||||
|  |   --ui-color-secondary-rgb: #{color.channel($secondary-color, "red")}, #{color.channel($secondary-color, "green")}, #{color.channel($secondary-color, "blue")}; | ||||||
|  |   --ui-color-background-rgb: #{color.channel($background-color, "red")}, #{color.channel($background-color, "green")}, #{color.channel($background-color, "blue")}; | ||||||
|  |   --ui-color-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||||
|  |   --ui-color-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Menu colors RGB versions */ | ||||||
|  |   --ui-color-menu-bg-rgb: #{color.channel($background-color, "red")}, #{color.channel($background-color, "green")}, #{color.channel($background-color, "blue")}; | ||||||
|  |   --ui-color-menu-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||||
|  |   --ui-color-menu-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Content colors RGB versions */ | ||||||
|  |   --ui-color-content-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||||
|  |   --ui-color-content-bg-rgb: #{color.channel($content-bg-color, "red")}, #{color.channel($content-bg-color, "green")}, #{color.channel($content-bg-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Icon colors RGB versions */ | ||||||
|  |   --ui-color-icon-primary-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||||
|  |   --ui-color-icon-secondary-rgb: #{color.channel($secondary-color, "red")}, #{color.channel($secondary-color, "green")}, #{color.channel($secondary-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Menu active RGB version */ | ||||||
|  |   --ui-color-menu-active-rgb: #{color.channel($menu-active-color, "red")}, #{color.channel($menu-active-color, "green")}, #{color.channel($menu-active-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Card colors RGB versions */ | ||||||
|  |   --ui-color-card-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||||
|  |   --ui-color-card-title-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||||
|  |   --ui-color-card-bg-rgb: #{color.channel($card-bg-color, "red")}, #{color.channel($card-bg-color, "green")}, #{color.channel($card-bg-color, "blue")}; | ||||||
|  |   --ui-color-card-text-rgb: #{color.channel($card-text-color, "red")}, #{color.channel($card-text-color, "green")}, #{color.channel($card-text-color, "blue")}; | ||||||
|  |   --ui-color-card-footer-bg-rgb: #{color.channel($card-footer-bg-color, "red")}, #{color.channel($card-footer-bg-color, "green")}, #{color.channel($card-footer-bg-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Status colors RGB versions */ | ||||||
|  |   --ui-color-success-rgb: #{color.channel($success-color, "red")}, #{color.channel($success-color, "green")}, #{color.channel($success-color, "blue")}; | ||||||
|  |   --ui-color-warning-rgb: #{color.channel($warning-color, "red")}, #{color.channel($warning-color, "green")}, #{color.channel($warning-color, "blue")}; | ||||||
|  |   --ui-color-danger-rgb: #{color.channel($danger-color, "red")}, #{color.channel($danger-color, "green")}, #{color.channel($danger-color, "blue")}; | ||||||
|  |   --ui-color-info-rgb: #{color.channel($info-color, "red")}, #{color.channel($info-color, "green")}, #{color.channel($info-color, "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Contrast colors RGB versions */ | ||||||
|  |   --ui-color-primary-contrast-rgb: #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "blue")}; | ||||||
|  |   --ui-color-secondary-contrast-rgb: #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "blue")}; | ||||||
|  |   --ui-color-success-contrast-rgb: #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "blue")}; | ||||||
|  |   --ui-color-warning-contrast-rgb: #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "blue")}; | ||||||
|  |   --ui-color-danger-contrast-rgb: #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "blue")}; | ||||||
|  |   --ui-color-info-contrast-rgb: #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Hard colors RGB versions */ | ||||||
|  |   --ui-color-primary-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-secondary-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-success-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-warning-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-danger-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-info-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Soft colors RGB versions */ | ||||||
|  |   --ui-color-primary-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-secondary-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-success-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-warning-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-danger-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "blue")}; | ||||||
|  |   --ui-color-info-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "blue")}; | ||||||
|  | 
 | ||||||
|  |   /* Note: hover and icon-muted use rgba(), so they already have alpha transparency built-in */ | ||||||
|  |   /* For hover effects, use the HEX version: var(--ui-color-hover) */ | ||||||
|  |   /* For icon-muted, use the HEX version: var(--ui-color-icon-muted) */ | ||||||
|  | 
 | ||||||
|  |   /* Card spacing and layout variables */ | ||||||
|  |   --ui-spacing-xs: 4px; | ||||||
|  |   --ui-spacing-sm: 8px; | ||||||
|  |   --ui-spacing-md: 16px; | ||||||
|  |   --ui-spacing-lg: 24px; | ||||||
|  |   --ui-border-radius: 8px; | ||||||
|  |   --ui-font-size-lg: 18px; | ||||||
|  |   --ui-font-weight-semibold: 600; | ||||||
|  |   --ui-card-header-min-height: 40px; | ||||||
|  | 
 | ||||||
|  |   /* Card shadows */ | ||||||
|  |   --ui-shadow-card: 0 2px 4px rgba(0, 0, 0, 0.1); | ||||||
|  |   --ui-shadow-card-hover: 0 4px 8px rgba(0, 0, 0, 0.15); | ||||||
|  | 
 | ||||||
|  |   /* Input component variables */ | ||||||
|  |   --ui-color-input-bg: #{color-mix(in srgb, #{$background} 95%, white 5%)}; | ||||||
|  |   --ui-color-input-text: #{color-mix(in srgb, #{$text} 95%, white 5%)}; | ||||||
|  |   --ui-color-input-placeholder: #{color-mix(in srgb, #{$primary} 50%, transparent 50%)}; | ||||||
|  |   --ui-color-input-label-focus: #{color-mix(in srgb, #{$primary} 95%, white 5%)}; | ||||||
|  |   --ui-color-input-label-invalid: #dc3545; | ||||||
|  | 
 | ||||||
|  |   /* Input box-shadow borders and effects */ | ||||||
|  |   --ui-shadow-input-border: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 1px #{$border}; | ||||||
|  |   --ui-shadow-input-focus: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 2px #{$primary}; | ||||||
|  |   --ui-shadow-input-active: 0 0 0 10px #{$primary}, 0 0 0 9px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}; | ||||||
|  |   --ui-shadow-input-invalid: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 2px #dc3545; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -50,11 +219,11 @@ | ||||||
|   background: var(--ui-color-menu-bg); |   background: var(--ui-color-menu-bg); | ||||||
|   color: var(--ui-color-menu-text); |   color: var(--ui-color-menu-text); | ||||||
|   border-right: 1px solid var(--ui-color-menu-border); |   border-right: 1px solid var(--ui-color-menu-border); | ||||||
|    | 
 | ||||||
|   &.active { |   &.active { | ||||||
|     background: var(--ui-color-menu-active); |     background: var(--ui-color-menu-active); | ||||||
|   } |   } | ||||||
|    | 
 | ||||||
|   &.focus, |   &.focus, | ||||||
|   &:active, |   &:active, | ||||||
|   &:hover { |   &:hover { | ||||||
|  | @ -83,4 +252,31 @@ | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Mixin that applies theme-aware styles to card components | ||||||
|  |  */ | ||||||
|  | @mixin ui-card-theme() { | ||||||
|  |   background: var(--ui-color-card-bg); | ||||||
|  |   border: 1px solid var(--ui-color-card-border); | ||||||
|  |   border-radius: var(--ui-border-radius); | ||||||
|  |   box-shadow: var(--ui-shadow-card); | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     box-shadow: var(--ui-shadow-card-hover); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .ui-card-title { | ||||||
|  |     color: var(--ui-color-card-title); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .ui-card-content { | ||||||
|  |     color: var(--ui-color-card-text); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   .ui-card-footer { | ||||||
|  |     background: var(--ui-color-card-footer-bg); | ||||||
|  |     border-top-color: var(--ui-color-card-border); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,150 @@ | ||||||
|  | /* Examples of using ui-contrast-color function in UI Library */ | ||||||
|  | 
 | ||||||
|  | @use 'sass:color'; | ||||||
|  | @use 'theme-mixins' as theme; | ||||||
|  | 
 | ||||||
|  | /* Example 1: Basic usage with predefined colors */ | ||||||
|  | .example-button { | ||||||
|  |   $button-bg: #007bff; // Blue background | ||||||
|  |   $light-text: #ffffff; // White text for dark backgrounds | ||||||
|  |   $dark-text: #333333;  // Dark text for light backgrounds | ||||||
|  |    | ||||||
|  |   background-color: $button-bg; | ||||||
|  |   color: theme.ui-contrast-color($button-bg, $light-text, $dark-text); | ||||||
|  |   // Result: white text because blue is darker than 50% lightness | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Example 2: Using with theme variables */ | ||||||
|  | .example-card { | ||||||
|  |   $card-bg: var(--ui-color-primary); | ||||||
|  |    | ||||||
|  |   // In SCSS context, you need to use actual color values | ||||||
|  |   $primary-color: #2c3e50; // This would come from your theme | ||||||
|  |   $text-light: #ffffff; | ||||||
|  |   $text-dark: #333333; | ||||||
|  |    | ||||||
|  |   background-color: $primary-color; | ||||||
|  |   color: theme.ui-contrast-color($primary-color, $text-light, $text-dark); | ||||||
|  |   // Result: white text because #2c3e50 is dark | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Example 3: Status colors with automatic contrast */ | ||||||
|  | .status-examples { | ||||||
|  |   .success-badge { | ||||||
|  |     $success: #28a745; // Green | ||||||
|  |     $light: #ffffff; | ||||||
|  |     $dark: #000000; | ||||||
|  |      | ||||||
|  |     background-color: $success; | ||||||
|  |     color: theme.ui-contrast-color($success, $light, $dark); | ||||||
|  |     // Result: white text because green is relatively dark | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .warning-badge { | ||||||
|  |     $warning: #ffc107; // Yellow | ||||||
|  |     $light: #ffffff; | ||||||
|  |     $dark: #000000; | ||||||
|  |      | ||||||
|  |     background-color: $warning; | ||||||
|  |     color: theme.ui-contrast-color($warning, $light, $dark); | ||||||
|  |     // Result: black text because yellow is light (>50% lightness) | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .danger-badge { | ||||||
|  |     $danger: #dc3545; // Red | ||||||
|  |     $light: #ffffff; | ||||||
|  |     $dark: #000000; | ||||||
|  |      | ||||||
|  |     background-color: $danger; | ||||||
|  |     color: theme.ui-contrast-color($danger, $light, $dark); | ||||||
|  |     // Result: white text because red is dark | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   .info-badge { | ||||||
|  |     $info: #17a2b8; // Cyan | ||||||
|  |     $light: #ffffff; | ||||||
|  |     $dark: #000000; | ||||||
|  |      | ||||||
|  |     background-color: $info; | ||||||
|  |     color: theme.ui-contrast-color($info, $light, $dark); | ||||||
|  |     // Result: white text because cyan is dark | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Example 4: Using with different light/dark colors based on theme */ | ||||||
|  | .theme-aware-component { | ||||||
|  |   // For light theme | ||||||
|  |   $bg-light: #f8f9fa; | ||||||
|  |   $text-light: #333333; | ||||||
|  |   $bg-dark: #343a40; | ||||||
|  |   $text-dark: #ffffff; | ||||||
|  |    | ||||||
|  |   // Component background | ||||||
|  |   $component-bg: #007bff; | ||||||
|  |    | ||||||
|  |   // In light theme context | ||||||
|  |   &.light-theme { | ||||||
|  |     background-color: $component-bg; | ||||||
|  |     color: theme.ui-contrast-color($component-bg, $text-dark, $text-light); | ||||||
|  |     // For light theme: if bg is light, use dark text; if bg is dark, use light text | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   // In dark theme context | ||||||
|  |   &.dark-theme { | ||||||
|  |     background-color: $component-bg; | ||||||
|  |     color: theme.ui-contrast-color($component-bg, $text-dark, $bg-light); | ||||||
|  |     // For dark theme: if bg is light, use light bg color; if bg is dark, use dark text | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Example 5: Dynamic contrast with CSS variables (conceptual) */ | ||||||
|  | /* | ||||||
|  | Note: The ui-contrast-color function works at compile time with SCSS variables. | ||||||
|  | For runtime CSS variables, you would use the pre-computed contrast variables: | ||||||
|  | 
 | ||||||
|  | .dynamic-component { | ||||||
|  |   background-color: var(--ui-color-primary); | ||||||
|  |   color: var(--ui-color-primary-contrast); | ||||||
|  |    | ||||||
|  |   // Or with RGB for transparency: | ||||||
|  |   background-color: rgba(var(--ui-color-primary-rgb), 0.8); | ||||||
|  |   color: var(--ui-color-primary-contrast); | ||||||
|  | } | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /* Example 6: Complex usage with mixed colors */ | ||||||
|  | .complex-example { | ||||||
|  |   $base-colors: ( | ||||||
|  |     'primary': #2c3e50, | ||||||
|  |     'secondary': #6c757d, | ||||||
|  |     'light': #f8f9fa, | ||||||
|  |     'dark': #343a40 | ||||||
|  |   ); | ||||||
|  |    | ||||||
|  |   $text-light: #ffffff; | ||||||
|  |   $text-dark: #333333; | ||||||
|  |    | ||||||
|  |   @each $name, $color in $base-colors { | ||||||
|  |     .#{$name}-variant { | ||||||
|  |       background-color: $color; | ||||||
|  |       color: theme.ui-contrast-color($color, $text-light, $text-dark); | ||||||
|  |        | ||||||
|  |       // Border with semi-transparent contrast color | ||||||
|  |       $contrast: theme.ui-contrast-color($color, $text-light, $text-dark); | ||||||
|  |       border: 2px solid rgba($contrast, 0.3); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* How the function works: | ||||||
|  |  *  | ||||||
|  |  * ui-contrast-color($base-color, $light-color, $dark-color) | ||||||
|  |  *  | ||||||
|  |  * 1. Calculates lightness of $base-color using HSL color space | ||||||
|  |  * 2. If lightness > 50% (closer to white): returns $dark-color | ||||||
|  |  * 3. If lightness ≤ 50% (closer to black): returns $light-color | ||||||
|  |  *  | ||||||
|  |  * This ensures optimal contrast for readability: | ||||||
|  |  * - Light backgrounds get dark text | ||||||
|  |  * - Dark backgrounds get light text | ||||||
|  |  */ | ||||||
|  | @ -17,3 +17,6 @@ export * from './lib/modules/form/components/input/input.component'; | ||||||
| // Components
 | // Components
 | ||||||
| export * from './lib/components/button/button.component'; | export * from './lib/components/button/button.component'; | ||||||
| export * from './lib/components/card/card.component'; | export * from './lib/components/card/card.component'; | ||||||
|  | export * from './lib/components/card/card-title/card-title.component'; | ||||||
|  | export * from './lib/components/card/card-menu/card-menu.component'; | ||||||
|  | export * from './lib/components/card/card-footer/card-footer.component'; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue