Skip to content

Commit

Permalink
feat(home): add generator component
Browse files Browse the repository at this point in the history
  • Loading branch information
pawcoding committed Feb 15, 2024
1 parent 86e1bb7 commit 6fcba53
Show file tree
Hide file tree
Showing 13 changed files with 323 additions and 11 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"private": true,
"dependencies": {
"@angular/animations": "^17.2.1",
"@angular/cdk": "17.1.2",
"@angular/cdk": "^17.1.2",
"@angular/common": "^17.2.1",
"@angular/compiler": "^17.2.1",
"@angular/core": "^17.2.1",
Expand Down
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/app/home/home.component.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
<rp-home-generator />

<div class="max-w-screen-md px-4 mx-auto sm:px-6 lg:px-8">
<hr class="border-t-neutral-300" />
</div>

<rp-home-manual />

<div class="max-w-screen-md px-4 mx-auto sm:px-6 lg:px-8">
<hr class="border-t-neutral-300" />
</div>

<rp-home-support />
3 changes: 2 additions & 1 deletion src/app/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Component } from '@angular/core';
import { HomeGeneratorComponent } from './ui/home-generator/home-generator.component';
import { HomeManualComponent } from './ui/home-manual/home-manual.component';
import { HomeSupportComponent } from './ui/home-support/home-support.component';

@Component({
selector: 'rp-home',
standalone: true,
imports: [HomeManualComponent, HomeSupportComponent],
imports: [HomeGeneratorComponent, HomeManualComponent, HomeSupportComponent],
templateUrl: './home.component.html',
})
export default class HomeComponent {}
85 changes: 85 additions & 0 deletions src/app/home/ui/home-generator/home-generator.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<section
class="px-4 py-8 flex-col sm:px-6 sm:py-12 lg:px-8 lg:py-16 min-h-[60svh] flex items-center justify-center"
>
<div class="max-w-lg mx-auto text-center">
<h2 class="text-2xl font-bold md:text-3xl">
{{ "home.generator.title" | translate }}
</h2>

<p class="mt-4 text-neutral-700 dark:text-neutral-400">
{{ "home.generator.description" | translate }}
</p>
</div>

<div class="w-full mx-auto mt-8 max-w-80">
<div>
<span
class="flex overflow-hidden border rounded-md shadow-sm border-neutral-200 dark:shadow-none dark:border-neutral-600"
>
<label for="color">
<span
[style.backgroundColor]="color()"
class="block w-10 h-full cursor-pointer"
></span>

<span class="sr-only">
{{ "home.generator.color" | translate }}
</span>
</label>

<input
#colorInput
type="color"
id="color"
class="hidden"
[value]="color()"
(input)="setColor(colorInput.value, $event)"
/>

<input
#hexInput
type="text"
class="w-full min-w-0 px-4 py-2 border-none focus:ring-0 focus:outline-none grow bg-neutral-100 dark:bg-neutral-600"
[value]="color()"
[placeholder]="'home.generator.color' | translate"
(input)="setColor(hexInput.value, $event)"
/>
</span>
</div>

<span
class="flex mt-2 overflow-hidden font-semibold rounded-md text-neutral-50"
[class]="
isValid()
? 'bg-blue-500 dark:bg-blue-600'
: 'bg-neutral-400 dark:bg-neutral-700'
"
>
<button
class="px-4 py-2 grow"
[class]="isValid() ? 'cursor-pointer' : 'cursor-default'"
[disabled]="!isValid()"
>
{{ "home.generator.generate" | translate }}
</button>

<rp-dropdown-menu
[items]="schemeOptions"
title="home.generator.scheme"
minWidth="20rem"
[disabled]="!isValid()"
>
<button class="flex items-center justify-center p-2">
<ng-icon [svg]="heroChevronDownMini" size="1.75rem" />
<span class="sr-only">
{{ "home.generator.scheme" | translate }}
</span>
</button>

<ng-template #itemTemplate let-scheme="item">
{{ scheme.label | translate }}
</ng-template>
</rp-dropdown-menu>
</span>
</div>
</section>
23 changes: 23 additions & 0 deletions src/app/home/ui/home-generator/home-generator.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { HomeGeneratorComponent } from './home-generator.component';

describe('HomeGeneratorComponent', () => {
let component: HomeGeneratorComponent;
let fixture: ComponentFixture<HomeGeneratorComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeGeneratorComponent]
})
.compileComponents();

fixture = TestBed.createComponent(HomeGeneratorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
66 changes: 66 additions & 0 deletions src/app/home/ui/home-generator/home-generator.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Component, effect, model, signal, viewChild } from '@angular/core';
import { NgIconComponent } from '@ng-icons/core';
import { heroChevronDownMini } from '@ng-icons/heroicons/mini';
import { TranslateModule } from '@ngx-translate/core';
import { DropdownMenuComponent } from '../../../shared/ui/dropdown-menu/dropdown-menu.component';

@Component({
selector: 'rp-home-generator',
standalone: true,
imports: [TranslateModule, DropdownMenuComponent, NgIconComponent],
templateUrl: './home-generator.component.html',
})
export class HomeGeneratorComponent {
public readonly color = model('#3B82F6');

protected readonly heroChevronDownMini = heroChevronDownMini;

protected readonly isValid = signal(true);

private readonly _hexInput = viewChild.required<{
nativeElement: HTMLInputElement;
}>('hexInput');

constructor() {
effect(() => {
this._hexInput().nativeElement.value = this.color();
});
}

protected get schemeOptions() {
return [
{ value: 'rainbow', label: 'scheme.rainbow' },
{ value: 'random', label: 'scheme.random' },
];
}

protected setColor(color: string, $event: Event): void {
// Remove leading and trailing whitespace
color = color.trim();

// Remove characters that are not valid hex color characters
color = color.replace(/[^0-9a-fA-F]/g, '');

// Add leading hash if missing
if (!color.startsWith('#')) {
color = `#${color}`;
}

// Normalize to uppercase and remove extra characters
color = color.substring(0, 7);
color = color.toUpperCase();

// Set color if it is a valid hex color
if (color.length === 4) {
this.color.set(color);
this.isValid.set(true);
} else if (color.length === 7) {
this.color.set(color);
this.isValid.set(true);
} else {
this.isValid.set(false);
}

this._hexInput().nativeElement.value = color;
}
}
4 changes: 3 additions & 1 deletion src/app/layout/layout.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<div class="w-[100dvw] h-[100dvh] grid grid-rows-[auto_1fr_auto]">
<div
class="w-full h-[100dvh] grid grid-rows-[auto_1fr] sm:grid-rows-[auto_1fr_auto]"
>
<header class="flex items-center justify-between w-full gap-2 p-4">
<h1>
<span class="sr-only">Rainbow Palette</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<rp-dropdown-menu
[items]="languageOptions"
title="layout.options.language"
closeOnScroll="false"
(selectedItemChange)="changeLanguage($event.value)"
>
<button
Expand Down Expand Up @@ -38,6 +39,7 @@
<rp-dropdown-menu
[items]="themeOptions"
title="layout.options.theme"
closeOnScroll="false"
(selectedItemChange)="changeTheme($event.value)"
>
<button
Expand Down
25 changes: 21 additions & 4 deletions src/app/shared/ui/dropdown-menu/dropdown-menu.component.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
<div [cdkMenuTriggerFor]="menu" [cdkMenuPosition]="menuPositions">
<div
[cdkMenuTriggerFor]="disabled() ? undefined : menu"
[cdkMenuPosition]="menuPositions"
[class]="!disabled() ? 'cursor-pointer' : 'cursor-default'"
>
<ng-content />
</div>

<ng-template #menu>
<div
cdkMenu
class="inline-flex flex-col w-full max-w-screen-sm gap-1 p-2 border rounded shadow-md bg-neutral-100 border-neutral-200 min-w-48 dark:bg-neutral-700 dark:border-neutral-600 dark:shadow-none"
class="inline-flex flex-col w-full gap-1 p-2 border rounded shadow-md bg-neutral-100 border-neutral-200 dark:bg-neutral-700 dark:border-neutral-600 dark:shadow-none"
[style.minWidth]="minWidth()"
[style.maxWidth]="maxWidth()"
>
@if (title()) {
<section>
Expand All @@ -17,20 +23,31 @@
</section>
}

<section cdkMenuGroup class="overflow-auto max-h-64">
<section
#menuGroup
cdkMenuGroup
class="overflow-auto"
[style.minHeight]="minHeight()"
[style.maxHeight]="maxHeight()"
>
@for (item of items(); track $index) {

<button
class="flex items-center justify-start w-full gap-2 p-2 transition-all duration-200 ease-in-out rounded-sm hover:shadow-sm hover:bg-neutral-200 dark:hover:shadow-none dark:hover:bg-neutral-600"
cdkMenuItemRadio
[cdkMenuItemChecked]="selectedItem() === item"
(cdkMenuItemTriggered)="select(item)"
>
<ng-container
[ngTemplateOutlet]="itemTemplate()"
[ngTemplateOutlet]="itemTemplate() ?? stringTemplate"
[ngTemplateOutletContext]="{ item: item }"
/>
</button>
}
</section>
</div>
</ng-template>

<ng-template #stringTemplate let-item="item">
{{ item | translate }}
</ng-template>
Loading

0 comments on commit 6fcba53

Please sign in to comment.