Add Color Picker component with interactive color wheel and hue selection
Features: - Interactive color wheel for saturation/lightness selection - Horizontal hue bar for full spectrum selection (0-360°) - RGB/HSL/HEX color model support with conversion utilities - ControlValueAccessor for Angular reactive forms integration - Canvas API rendering for smooth color gradients - Touch and mouse event support for intuitive color selection - Keyboard navigation with arrow keys for accessibility - Multiple size variants (sm, md, lg) and color themes - Comprehensive demo with preset colors and interactive examples - Full semantic design token integration following project standards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,339 @@
|
||||
@use '../../../../../shared-ui/src/styles/semantic/index' as *;
|
||||
|
||||
.demo-container {
|
||||
padding: $semantic-spacing-layout-section-md;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
|
||||
h2 {
|
||||
font-family: map-get($semantic-typography-heading-h2, font-family);
|
||||
font-size: map-get($semantic-typography-heading-h2, font-size);
|
||||
font-weight: map-get($semantic-typography-heading-h2, font-weight);
|
||||
line-height: map-get($semantic-typography-heading-h2, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin-bottom: $semantic-spacing-layout-section-lg;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-section {
|
||||
margin-bottom: $semantic-spacing-layout-section-xl;
|
||||
|
||||
h3 {
|
||||
font-family: map-get($semantic-typography-heading-h3, font-family);
|
||||
font-size: map-get($semantic-typography-heading-h3, font-size);
|
||||
font-weight: map-get($semantic-typography-heading-h3, font-weight);
|
||||
line-height: map-get($semantic-typography-heading-h3, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin-bottom: $semantic-spacing-layout-section-md;
|
||||
padding-bottom: $semantic-spacing-component-sm;
|
||||
border-bottom: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-family: map-get($semantic-typography-heading-h4, font-family);
|
||||
font-size: map-get($semantic-typography-heading-h4, font-size);
|
||||
font-weight: map-get($semantic-typography-heading-h4, font-weight);
|
||||
line-height: map-get($semantic-typography-heading-h4, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin-bottom: $semantic-spacing-component-md;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-row {
|
||||
display: flex;
|
||||
gap: $semantic-spacing-layout-section-md;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
@media (max-width: calc(#{$semantic-breakpoint-md} - 1px)) {
|
||||
flex-direction: column;
|
||||
gap: $semantic-spacing-layout-section-sm;
|
||||
|
||||
> * {
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.demo-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: $semantic-spacing-layout-section-md;
|
||||
|
||||
@media (max-width: calc(#{$semantic-breakpoint-sm} - 1px)) {
|
||||
grid-template-columns: 1fr;
|
||||
gap: $semantic-spacing-layout-section-sm;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-item {
|
||||
padding: $semantic-spacing-component-lg;
|
||||
background: $semantic-color-surface-secondary;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
border-radius: $semantic-border-radius-md;
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
}
|
||||
|
||||
.demo-info {
|
||||
padding: $semantic-spacing-component-lg;
|
||||
background: $semantic-color-surface-elevated;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
border-radius: $semantic-border-radius-md;
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
|
||||
p {
|
||||
font-family: map-get($semantic-typography-body-medium, font-family);
|
||||
font-size: map-get($semantic-typography-body-medium, font-size);
|
||||
font-weight: map-get($semantic-typography-body-medium, font-weight);
|
||||
line-height: map-get($semantic-typography-body-medium, line-height);
|
||||
color: $semantic-color-text-primary;
|
||||
margin-bottom: $semantic-spacing-component-sm;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: $semantic-typography-font-weight-semibold;
|
||||
color: $semantic-color-text-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
width: 100px;
|
||||
height: 60px;
|
||||
border: $semantic-border-width-2 solid $semantic-color-border-primary;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
margin-top: $semantic-spacing-component-sm;
|
||||
}
|
||||
|
||||
.demo-form {
|
||||
.demo-form-info {
|
||||
margin-top: $semantic-spacing-component-lg;
|
||||
padding: $semantic-spacing-component-md;
|
||||
background: $semantic-color-surface-container;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
|
||||
pre {
|
||||
font-family: $semantic-typography-font-family-mono;
|
||||
font-size: $semantic-typography-font-size-sm;
|
||||
color: $semantic-color-text-secondary;
|
||||
background: $semantic-color-surface-primary;
|
||||
padding: $semantic-spacing-component-sm;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.demo-theme-preview {
|
||||
margin-top: $semantic-spacing-component-lg;
|
||||
|
||||
.theme-sample {
|
||||
padding: $semantic-spacing-component-lg;
|
||||
border-radius: $semantic-border-radius-md;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
text-align: center;
|
||||
font-family: map-get($semantic-typography-body-medium, font-family);
|
||||
font-size: map-get($semantic-typography-body-medium, font-size);
|
||||
font-weight: map-get($semantic-typography-body-medium, font-weight);
|
||||
line-height: map-get($semantic-typography-body-medium, line-height);
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.event-log {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
background: $semantic-color-surface-primary;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
padding: $semantic-spacing-component-sm;
|
||||
margin-bottom: $semantic-spacing-component-md;
|
||||
|
||||
.event-entry {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: $semantic-spacing-component-xs $semantic-spacing-component-sm;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
margin-bottom: $semantic-spacing-component-xs;
|
||||
font-family: $semantic-typography-font-family-mono;
|
||||
font-size: $semantic-typography-font-size-sm;
|
||||
transition: background-color $semantic-motion-duration-fast $semantic-motion-easing-ease;
|
||||
|
||||
&--recent {
|
||||
background-color: $semantic-color-surface-elevated;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-focus;
|
||||
}
|
||||
|
||||
.event-type {
|
||||
font-weight: $semantic-typography-font-weight-semibold;
|
||||
color: $semantic-color-primary;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.event-value {
|
||||
flex: 1;
|
||||
color: $semantic-color-text-primary;
|
||||
margin: 0 $semantic-spacing-component-sm;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.event-time {
|
||||
color: $semantic-color-text-tertiary;
|
||||
font-size: $semantic-typography-font-size-xs;
|
||||
min-width: 80px;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.clear-log-btn {
|
||||
padding: $semantic-spacing-component-xs $semantic-spacing-component-sm;
|
||||
background: $semantic-color-danger;
|
||||
color: $semantic-color-on-danger;
|
||||
border: none;
|
||||
border-radius: $semantic-border-button-radius;
|
||||
font-family: map-get($semantic-typography-button-small, font-family);
|
||||
font-size: map-get($semantic-typography-button-small, font-size);
|
||||
font-weight: map-get($semantic-typography-button-small, font-weight);
|
||||
line-height: map-get($semantic-typography-button-small, line-height);
|
||||
cursor: pointer;
|
||||
transition: all $semantic-motion-duration-fast $semantic-motion-easing-ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: $semantic-shadow-button-hover;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
box-shadow: $semantic-shadow-button-rest;
|
||||
}
|
||||
}
|
||||
|
||||
.preset-colors {
|
||||
padding: $semantic-spacing-component-lg;
|
||||
background: $semantic-color-surface-elevated;
|
||||
border: $semantic-border-width-1 solid $semantic-color-border-subtle;
|
||||
border-radius: $semantic-border-radius-md;
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
|
||||
.preset-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(40px, 1fr));
|
||||
gap: $semantic-spacing-component-sm;
|
||||
max-width: 400px;
|
||||
margin-top: $semantic-spacing-component-md;
|
||||
}
|
||||
|
||||
.preset-color-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: $semantic-border-width-2 solid $semantic-color-border-primary;
|
||||
border-radius: $semantic-border-radius-sm;
|
||||
cursor: pointer;
|
||||
transition: all $semantic-motion-duration-fast $semantic-motion-easing-ease;
|
||||
box-shadow: $semantic-shadow-elevation-1;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: $semantic-shadow-elevation-3;
|
||||
border-color: $semantic-color-border-focus;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 2px solid $semantic-color-focus;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(1.05);
|
||||
box-shadow: $semantic-shadow-elevation-2;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive adjustments
|
||||
@media (max-width: calc(#{$semantic-breakpoint-md} - 1px)) {
|
||||
.demo-container {
|
||||
padding: $semantic-spacing-layout-section-sm;
|
||||
}
|
||||
|
||||
.demo-item {
|
||||
padding: $semantic-spacing-component-md;
|
||||
}
|
||||
|
||||
.demo-info {
|
||||
padding: $semantic-spacing-component-md;
|
||||
}
|
||||
|
||||
.preset-colors {
|
||||
padding: $semantic-spacing-component-md;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: calc(#{$semantic-breakpoint-sm} - 1px)) {
|
||||
.demo-container {
|
||||
padding: $semantic-spacing-layout-section-xs;
|
||||
}
|
||||
|
||||
.demo-item {
|
||||
padding: $semantic-spacing-component-sm;
|
||||
}
|
||||
|
||||
.demo-info {
|
||||
padding: $semantic-spacing-component-sm;
|
||||
}
|
||||
|
||||
.preset-colors {
|
||||
padding: $semantic-spacing-component-sm;
|
||||
|
||||
.preset-grid {
|
||||
grid-template-columns: repeat(auto-fit, minmax(35px, 1fr));
|
||||
gap: $semantic-spacing-component-xs;
|
||||
}
|
||||
|
||||
.preset-color-btn {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.event-log {
|
||||
.event-entry {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: $semantic-spacing-component-xs;
|
||||
|
||||
.event-type,
|
||||
.event-value,
|
||||
.event-time {
|
||||
min-width: unset;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule, FormControl, FormGroup } from '@angular/forms';
|
||||
import { ColorPickerComponent } from '../../../../../ui-essentials/src/lib/components/forms/color-picker/color-picker.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-color-picker-demo',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
ColorPickerComponent
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<div class="demo-container">
|
||||
<h2>Color Picker Component Showcase</h2>
|
||||
|
||||
<!-- Basic Color Picker -->
|
||||
<section class="demo-section">
|
||||
<h3>Basic Color Picker</h3>
|
||||
<div class="demo-row">
|
||||
<ui-color-picker
|
||||
label="Choose a color"
|
||||
description="Select your preferred color"
|
||||
[(ngModel)]="basicColor"
|
||||
(colorChange)="onColorChange('basic', $event)"
|
||||
/>
|
||||
<div class="demo-info">
|
||||
<p><strong>Selected:</strong> {{ basicColor() }}</p>
|
||||
<div class="color-preview" [style.background-color]="basicColor()"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Size Variants -->
|
||||
<section class="demo-section">
|
||||
<h3>Size Variants</h3>
|
||||
<div class="demo-grid">
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Small (sm)"
|
||||
size="sm"
|
||||
[(ngModel)]="sizeSmall"
|
||||
(colorChange)="onColorChange('size-sm', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Medium (md)"
|
||||
size="md"
|
||||
[(ngModel)]="sizeMedium"
|
||||
(colorChange)="onColorChange('size-md', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Large (lg)"
|
||||
size="lg"
|
||||
[(ngModel)]="sizeLarge"
|
||||
(colorChange)="onColorChange('size-lg', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Color Variants -->
|
||||
<section class="demo-section">
|
||||
<h3>Color Variants</h3>
|
||||
<div class="demo-grid">
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Primary"
|
||||
variant="primary"
|
||||
[(ngModel)]="variantPrimary"
|
||||
(colorChange)="onColorChange('variant-primary', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Secondary"
|
||||
variant="secondary"
|
||||
[(ngModel)]="variantSecondary"
|
||||
(colorChange)="onColorChange('variant-secondary', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Success"
|
||||
variant="success"
|
||||
[(ngModel)]="variantSuccess"
|
||||
(colorChange)="onColorChange('variant-success', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Danger"
|
||||
variant="danger"
|
||||
[(ngModel)]="variantDanger"
|
||||
(colorChange)="onColorChange('variant-danger', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- States -->
|
||||
<section class="demo-section">
|
||||
<h3>States</h3>
|
||||
<div class="demo-grid">
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Default State"
|
||||
[(ngModel)]="stateDefault"
|
||||
(colorChange)="onColorChange('state-default', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Error State"
|
||||
state="error"
|
||||
description="This field has an error"
|
||||
[(ngModel)]="stateError"
|
||||
(colorChange)="onColorChange('state-error', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-item">
|
||||
<ui-color-picker
|
||||
label="Disabled State"
|
||||
[disabled]="true"
|
||||
[(ngModel)]="stateDisabled"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Required Field -->
|
||||
<section class="demo-section">
|
||||
<h3>Required Field</h3>
|
||||
<div class="demo-row">
|
||||
<ui-color-picker
|
||||
label="Brand Color"
|
||||
description="Choose your brand's primary color"
|
||||
[required]="true"
|
||||
[(ngModel)]="requiredColor"
|
||||
(colorChange)="onColorChange('required', $event)"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Reactive Forms Integration -->
|
||||
<section class="demo-section">
|
||||
<h3>Reactive Forms Integration</h3>
|
||||
<form [formGroup]="colorForm" class="demo-form">
|
||||
<div class="demo-row">
|
||||
<ui-color-picker
|
||||
label="Background Color"
|
||||
description="Select background color for your theme"
|
||||
formControlName="backgroundColor"
|
||||
(colorChange)="onColorChange('form-bg', $event)"
|
||||
/>
|
||||
<ui-color-picker
|
||||
label="Text Color"
|
||||
description="Select text color for your theme"
|
||||
formControlName="textColor"
|
||||
(colorChange)="onColorChange('form-text', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-form-info">
|
||||
<h4>Form Values:</h4>
|
||||
<pre>{{ getFormValues() }}</pre>
|
||||
</div>
|
||||
<div class="demo-theme-preview">
|
||||
<h4>Theme Preview:</h4>
|
||||
<div
|
||||
class="theme-sample"
|
||||
[style.background-color]="colorForm.get('backgroundColor')?.value || '#FFFFFF'"
|
||||
[style.color]="colorForm.get('textColor')?.value || '#000000'">
|
||||
Sample text with selected colors
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- Interactive Demo -->
|
||||
<section class="demo-section">
|
||||
<h3>Interactive Demo</h3>
|
||||
<div class="demo-row">
|
||||
<ui-color-picker
|
||||
label="Interactive Color"
|
||||
description="Watch the events in the console"
|
||||
[(ngModel)]="interactiveColor"
|
||||
(colorChange)="onInteractiveColorChange($event)"
|
||||
(colorFocus)="onColorFocus($event)"
|
||||
(colorBlur)="onColorBlur($event)"
|
||||
/>
|
||||
<div class="demo-info">
|
||||
<h4>Event Log:</h4>
|
||||
<div class="event-log">
|
||||
@for (event of eventLog(); track event.id) {
|
||||
<div class="event-entry" [class.event-entry--recent]="event.recent">
|
||||
<span class="event-type">{{ event.type }}</span>
|
||||
<span class="event-value">{{ event.value }}</span>
|
||||
<span class="event-time">{{ event.time }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<button class="clear-log-btn" (click)="clearEventLog()">Clear Log</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Preset Colors Demo -->
|
||||
<section class="demo-section">
|
||||
<h3>Preset Colors</h3>
|
||||
<div class="demo-row">
|
||||
<ui-color-picker
|
||||
label="Select or enter color"
|
||||
[(ngModel)]="presetColor"
|
||||
(colorChange)="onColorChange('preset', $event)"
|
||||
/>
|
||||
<div class="preset-colors">
|
||||
<h4>Quick Colors:</h4>
|
||||
<div class="preset-grid">
|
||||
@for (color of presetColors; track color.name) {
|
||||
<button
|
||||
class="preset-color-btn"
|
||||
[style.background-color]="color.hex"
|
||||
[attr.title]="color.name + ' (' + color.hex + ')'"
|
||||
(click)="selectPresetColor(color.hex)">
|
||||
<span class="sr-only">{{ color.name }}</span>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
`,
|
||||
styleUrl: './color-picker-demo.component.scss'
|
||||
})
|
||||
export class ColorPickerDemoComponent {
|
||||
// Basic demo colors
|
||||
basicColor = signal<string>('#3498db');
|
||||
|
||||
// Size variants
|
||||
sizeSmall = signal<string>('#e74c3c');
|
||||
sizeMedium = signal<string>('#2ecc71');
|
||||
sizeLarge = signal<string>('#f39c12');
|
||||
|
||||
// Color variants
|
||||
variantPrimary = signal<string>('#3498db');
|
||||
variantSecondary = signal<string>('#95a5a6');
|
||||
variantSuccess = signal<string>('#2ecc71');
|
||||
variantDanger = signal<string>('#e74c3c');
|
||||
|
||||
// States
|
||||
stateDefault = signal<string>('#9b59b6');
|
||||
stateError = signal<string>('#e74c3c');
|
||||
stateDisabled = signal<string>('#bdc3c7');
|
||||
|
||||
// Required field
|
||||
requiredColor = signal<string>('#1abc9c');
|
||||
|
||||
// Interactive demo
|
||||
interactiveColor = signal<string>('#34495e');
|
||||
eventLog = signal<Array<{id: number, type: string, value: string, time: string, recent: boolean}>>([]);
|
||||
private eventIdCounter = 0;
|
||||
|
||||
// Preset colors
|
||||
presetColor = signal<string>('#ff6b6b');
|
||||
presetColors = [
|
||||
{ name: 'Red', hex: '#e74c3c' },
|
||||
{ name: 'Orange', hex: '#f39c12' },
|
||||
{ name: 'Yellow', hex: '#f1c40f' },
|
||||
{ name: 'Green', hex: '#2ecc71' },
|
||||
{ name: 'Blue', hex: '#3498db' },
|
||||
{ name: 'Purple', hex: '#9b59b6' },
|
||||
{ name: 'Pink', hex: '#e91e63' },
|
||||
{ name: 'Teal', hex: '#1abc9c' },
|
||||
{ name: 'Gray', hex: '#95a5a6' },
|
||||
{ name: 'Black', hex: '#2c3e50' },
|
||||
{ name: 'White', hex: '#ecf0f1' },
|
||||
{ name: 'Brown', hex: '#8d4925' }
|
||||
];
|
||||
|
||||
// Reactive forms
|
||||
colorForm = new FormGroup({
|
||||
backgroundColor: new FormControl('#ffffff'),
|
||||
textColor: new FormControl('#333333')
|
||||
});
|
||||
|
||||
onColorChange(source: string, color: string): void {
|
||||
console.log(`Color changed (${source}):`, color);
|
||||
}
|
||||
|
||||
onInteractiveColorChange(color: string): void {
|
||||
this.addEventToLog('change', color);
|
||||
}
|
||||
|
||||
onColorFocus(event: FocusEvent): void {
|
||||
this.addEventToLog('focus', 'Color picker focused');
|
||||
}
|
||||
|
||||
onColorBlur(event: FocusEvent): void {
|
||||
this.addEventToLog('blur', 'Color picker blurred');
|
||||
}
|
||||
|
||||
private addEventToLog(type: string, value: string): void {
|
||||
const newEvent = {
|
||||
id: this.eventIdCounter++,
|
||||
type,
|
||||
value,
|
||||
time: new Date().toLocaleTimeString(),
|
||||
recent: true
|
||||
};
|
||||
|
||||
const currentLog = this.eventLog();
|
||||
const updatedLog = [newEvent, ...currentLog.slice(0, 9)].map((event, index) => ({
|
||||
...event,
|
||||
recent: index === 0
|
||||
}));
|
||||
|
||||
this.eventLog.set(updatedLog);
|
||||
|
||||
// Remove recent flag after animation
|
||||
setTimeout(() => {
|
||||
const logWithoutRecent = this.eventLog().map(event => ({
|
||||
...event,
|
||||
recent: false
|
||||
}));
|
||||
this.eventLog.set(logWithoutRecent);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
clearEventLog(): void {
|
||||
this.eventLog.set([]);
|
||||
this.eventIdCounter = 0;
|
||||
}
|
||||
|
||||
selectPresetColor(color: string): void {
|
||||
this.presetColor.set(color);
|
||||
}
|
||||
|
||||
getFormValues(): string {
|
||||
return JSON.stringify(this.colorForm.value, null, 2);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import { OverlayContainerDemoComponent } from './overlay-container-demo/overlay-
|
||||
import { LoadingSpinnerDemoComponent } from './loading-spinner-demo/loading-spinner-demo.component';
|
||||
import { ProgressCircleDemoComponent } from './progress-circle-demo/progress-circle-demo.component';
|
||||
import { RangeSliderDemoComponent } from './range-slider-demo/range-slider-demo.component';
|
||||
import { ColorPickerDemoComponent } from './color-picker-demo/color-picker-demo.component';
|
||||
import { DividerDemoComponent } from './divider-demo/divider-demo.component';
|
||||
import { TooltipDemoComponent } from './tooltip-demo/tooltip-demo.component';
|
||||
import { AccordionDemoComponent } from './accordion-demo/accordion-demo.component';
|
||||
@@ -55,6 +56,9 @@ import { StepperDemoComponent } from './stepper-demo/stepper-demo.component';
|
||||
import { FabMenuDemoComponent } from './fab-menu-demo/fab-menu-demo.component';
|
||||
import { EnhancedTableDemoComponent } from './enhanced-table-demo/enhanced-table-demo.component';
|
||||
import { SplitButtonDemoComponent } from './split-button-demo/split-button-demo.component';
|
||||
import { CommandPaletteDemoComponent } from './command-palette-demo/command-palette-demo.component';
|
||||
import { TransferListDemoComponent } from './transfer-list-demo/transfer-list-demo.component';
|
||||
import { FloatingToolbarDemoComponent } from './floating-toolbar-demo/floating-toolbar-demo.component';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -221,6 +225,10 @@ import { SplitButtonDemoComponent } from './split-button-demo/split-button-demo.
|
||||
<ui-range-slider-demo></ui-range-slider-demo>
|
||||
}
|
||||
|
||||
@case ("color-picker") {
|
||||
<ui-color-picker-demo></ui-color-picker-demo>
|
||||
}
|
||||
|
||||
@case ("divider") {
|
||||
<ui-divider-demo></ui-divider-demo>
|
||||
}
|
||||
@@ -273,6 +281,18 @@ import { SplitButtonDemoComponent } from './split-button-demo/split-button-demo.
|
||||
<ui-split-button-demo></ui-split-button-demo>
|
||||
}
|
||||
|
||||
@case ("command-palette") {
|
||||
<ui-command-palette-demo></ui-command-palette-demo>
|
||||
}
|
||||
|
||||
@case ("floating-toolbar") {
|
||||
<ui-floating-toolbar-demo></ui-floating-toolbar-demo>
|
||||
}
|
||||
|
||||
@case ("transfer-list") {
|
||||
<ui-transfer-list-demo></ui-transfer-list-demo>
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
`,
|
||||
@@ -287,8 +307,8 @@ import { SplitButtonDemoComponent } from './split-button-demo/split-button-demo.
|
||||
GridSystemDemoComponent, SpacerDemoComponent, ContainerDemoComponent, PaginationDemoComponent,
|
||||
SkeletonLoaderDemoComponent, EmptyStateDemoComponent, FileUploadDemoComponent, FormFieldDemoComponent,
|
||||
AutocompleteDemoComponent, BackdropDemoComponent, OverlayContainerDemoComponent, LoadingSpinnerDemoComponent,
|
||||
ProgressCircleDemoComponent, RangeSliderDemoComponent, DividerDemoComponent, TooltipDemoComponent, AccordionDemoComponent,
|
||||
PopoverDemoComponent, AlertDemoComponent, SnackbarDemoComponent, ToastDemoComponent, TreeViewDemoComponent, TimelineDemoComponent, StepperDemoComponent, FabMenuDemoComponent, EnhancedTableDemoComponent, SplitButtonDemoComponent]
|
||||
ProgressCircleDemoComponent, RangeSliderDemoComponent, ColorPickerDemoComponent, DividerDemoComponent, TooltipDemoComponent, AccordionDemoComponent,
|
||||
PopoverDemoComponent, AlertDemoComponent, SnackbarDemoComponent, ToastDemoComponent, TreeViewDemoComponent, TimelineDemoComponent, StepperDemoComponent, FabMenuDemoComponent, EnhancedTableDemoComponent, SplitButtonDemoComponent, CommandPaletteDemoComponent, FloatingToolbarDemoComponent, TransferListDemoComponent]
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
faGripVertical, faArrowsAlt, faBoxOpen, faChevronLeft, faSpinner, faExclamationTriangle,
|
||||
faCloudUploadAlt, faFileText, faListAlt, faCircle, faExpandArrowsAlt, faCircleNotch, faSliders,
|
||||
faMinus, faInfoCircle, faChevronDown, faCaretUp, faExclamationCircle, faSitemap, faStream,
|
||||
faBell, faRoute, faChevronUp, faEllipsisV, faCut
|
||||
faBell, faRoute, faChevronUp, faEllipsisV, faCut, faPalette, faExchangeAlt, faTools
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { DemoRoutes } from '../../demos';
|
||||
import { ScrollContainerComponent } from '../../../../../ui-essentials/src/lib/layouts/scroll-container.component';
|
||||
@@ -104,6 +104,9 @@ export class DashboardComponent {
|
||||
faChevronUp = faChevronUp;
|
||||
faEllipsisV = faEllipsisV;
|
||||
faCut = faCut;
|
||||
faPalette = faPalette;
|
||||
faExchangeAlt = faExchangeAlt;
|
||||
faTools = faTools;
|
||||
|
||||
menuItems: any = []
|
||||
|
||||
@@ -149,7 +152,8 @@ export class DashboardComponent {
|
||||
this.createChildItem("time-picker", "Time Picker", this.faClock),
|
||||
this.createChildItem("file-upload", "File Upload", this.faCloudUploadAlt),
|
||||
this.createChildItem("form-field", "Form Field", this.faFileText),
|
||||
this.createChildItem("range-slider", "Range Slider", this.faSliders)
|
||||
this.createChildItem("range-slider", "Range Slider", this.faSliders),
|
||||
this.createChildItem("color-picker", "Color Picker", this.faPalette)
|
||||
];
|
||||
this.addMenuItem("forms", "Forms", this.faEdit, formsChildren);
|
||||
|
||||
@@ -166,7 +170,8 @@ export class DashboardComponent {
|
||||
this.createChildItem("divider", "Divider", this.faMinus),
|
||||
this.createChildItem("timeline", "Timeline", this.faStream),
|
||||
this.createChildItem("tooltip", "Tooltip", this.faInfoCircle),
|
||||
this.createChildItem("tree-view", "Tree View", this.faSitemap)
|
||||
this.createChildItem("tree-view", "Tree View", this.faSitemap),
|
||||
this.createChildItem("transfer-list", "Transfer List", this.faExchangeAlt)
|
||||
];
|
||||
this.addMenuItem("data-display", "Data Display", this.faEye, dataDisplayChildren);
|
||||
|
||||
@@ -224,7 +229,9 @@ export class DashboardComponent {
|
||||
this.createChildItem("drawer", "Drawer/Sidebar", this.faBars),
|
||||
this.createChildItem("popover", "Popover/Dropdown", this.faCaretUp),
|
||||
this.createChildItem("backdrop", "Backdrop", this.faCircle),
|
||||
this.createChildItem("overlay-container", "Overlay Container", this.faExpandArrowsAlt)
|
||||
this.createChildItem("overlay-container", "Overlay Container", this.faExpandArrowsAlt),
|
||||
this.createChildItem("command-palette", "Command Palette", this.faPalette),
|
||||
this.createChildItem("floating-toolbar", "Floating Toolbar", this.faTools)
|
||||
];
|
||||
this.addMenuItem("overlays", "Overlays", this.faLayerGroup, overlaysChildren);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user