Skip to content

Commit

Permalink
Create improved book toggling component
Browse files Browse the repository at this point in the history
  • Loading branch information
Nateowami committed Aug 21, 2024
1 parent 0d9b08d commit 7224510
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,12 @@
}
<div class="flex-row">
@for (book of bookOptions; track book) {
<mat-chip-listbox
hideSingleSelectionIndicator
[selectable]="!readonly"
class="book-multi-select"
<app-toggle-book
[book]="book.bookNum"
[selected]="book.selected"
(selected)="onChipListChange(book)"
[disabled]="readonly"
(change)="onChipListChange(book)"
>
<mat-chip-option [value]="book" [selected]="book.selected">
{{ "canon.book_names." + book.bookId | transloco }}
</mat-chip-option>
</mat-chip-listbox>
></app-toggle-book>
}
</div>
</ng-container>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MatChipsModule } from '@angular/material/chips';
import { TranslocoModule } from '@ngneat/transloco';
import { Canon } from '@sillsdev/scripture';
import { UICommonModule } from 'xforge-common/ui-common.module';
import { ToggleBookComponent } from '../../translate/draft-generation/toggle-book/toggle-book.component';

export interface BookOption {
bookNum: number;
Expand All @@ -14,7 +15,7 @@ export interface BookOption {
selector: 'app-book-multi-select',
templateUrl: './book-multi-select.component.html',
standalone: true,
imports: [UICommonModule, MatChipsModule, TranslocoModule],
imports: [UICommonModule, MatChipsModule, TranslocoModule, ToggleBookComponent],
styleUrls: ['./book-multi-select.component.scss']
})
export class BookMultiSelectComponent implements OnChanges {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<ng-container transloco>
<span class="wrapper" [style.--background-image]="backgroundCssGradientStripes">
<span
class="book"
[style.--progress]="progressCssValue"
[style.--border-width]="borderWidthCssValue"
[class.selected]="selected"
[class.disabled]="disabled"
(click)="toggleSelected()"
(keypress)="onKeyPress($event)"
[matTooltip]="progressDescription"
matRipple
[matRippleDisabled]="disabled"
[tabindex]="disabled ? -1 : 0"
>
{{ bookName(book) }}
</span>
</span>
</ng-container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
:host {
--progress-color: hsl(0, 0%, 80%);
--progress-hover-color: hsl(0, 0%, 70%);
--progress-bg-color: hsl(0, 0%, 90%);
--progress-hover-bg-color: hsl(0, 0%, 80%);

user-select: none;
}

.wrapper {
display: inline-block;
border-radius: 1000px;
}

.wrapper:has(.selected) {
background-image: var(--background-image);
}

.book {
display: block;
background-color: var(--progress-bg-color);
padding: 0 16px;
line-height: 32px;
border-radius: 1000px;
margin: var(--border-width);
position: relative;

background-image: linear-gradient(
90deg,
var(--progress-color) var(--progress),
var(--progress-bg-color) var(--progress)
);

&.disabled:not(.selected) {
opacity: 0.7;
}

&:not(.disabled) {
cursor: pointer;
}

&:hover:not(.disabled),
&:focus:not(.disabled) {
outline: none;
background-image: linear-gradient(
90deg,
var(--progress-hover-color) var(--progress),
var(--progress-hover-bg-color) var(--progress)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatRippleModule } from '@angular/material/core';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslocoModule } from '@ngneat/transloco';
import { I18nService } from '../../../../xforge-common/i18n.service';

@Component({
selector: 'app-toggle-book',
standalone: true,
imports: [TranslocoModule, MatTooltipModule, MatRippleModule],
templateUrl: './toggle-book.component.html',
styleUrl: './toggle-book.component.scss'
})
export class ToggleBookComponent {
@Output() selectedChanged = new EventEmitter<number>();
@Input() selected = false;
@Input() disabled = false;
@Input() borderWidth = 2;
@Input() book!: number;
@Input() progress?: number;
@Input() hues: number[] = [230];

constructor(private readonly i18n: I18nService) {}

bookName(book: number): string {
return this.i18n.localizeBook(book);
}

toggleSelected(): void {
if (!this.disabled) {
this.selected = !this.selected;
this.selectedChanged.emit(this.book);
}
}

onKeyPress(event: KeyboardEvent): void {
if (event.key === 'Enter' || event.key === ' ') {
this.toggleSelected();
event.preventDefault();
}
}

get backgroundCssGradientStripes(): string {
const percentPerStripe = 12.5;
const colors = this.hues.map(hue => `hsl(${hue}, 80%, 60%)`);
let gradient = [];
for (const [index, color] of colors.entries()) {
const from = index * percentPerStripe;
const to = (index + 1) * percentPerStripe;
gradient.push(`${color} ${from}%, ${color} ${to}%`);
}
return `repeating-linear-gradient(135deg, ${gradient.join(', ')})`;
}

get progressCssValue(): string {
return `${(this.progress ?? 0) * 100}%`;
}

get borderWidthCssValue(): string {
return `${this.borderWidth}px`;
}

get progressDescription(): string {
if (this.progress == null) return '';

// avoid showing 100% when it's not quite there
let percent = this.progress > 0.99 && this.progress < 1 ? 99 : Math.round(this.progress * 100);
return this.progress != null ? `${Math.round(percent)}% translated` : '';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { CommonModule } from '@angular/common';
import { TranslocoModule } from '@ngneat/transloco';
import { Meta, moduleMetadata, StoryObj } from '@storybook/angular';
import { TranslocoMarkupModule } from 'ngx-transloco-markup';
import { I18nStoryModule } from '../../../../xforge-common/i18n-story.module';
import { UICommonModule } from '../../../../xforge-common/ui-common.module';
import { ToggleBookComponent } from './toggle-book.component';

const meta: Meta = {
title: 'Translate/ToggleBook',
component: ToggleBookComponent,
decorators: [
moduleMetadata({
imports: [UICommonModule, I18nStoryModule, CommonModule, TranslocoModule, TranslocoMarkupModule]
})
],
argTypes: {
progress: { control: { type: 'range', min: 0, max: 1, step: 0.01 } }
}
};

export default meta;

interface StoryState {
book: number;
progress?: number;
hues: number[];
selected: boolean;
disabled: boolean;
}

type Story = StoryObj<StoryState>;

export const Default: Story = {
args: {
book: 1,
progress: 0.37,
hues: [0]
}
};

export const Selected: Story = {
args: {
...Default.args,
selected: true
}
};

export const TwoColor: Story = {
args: {
...Selected.args,
hues: [0, 240]
}
};

export const ThreeColor: Story = {
args: {
...Selected.args,
hues: [0, 120, 240]
}
};

export const Disabled: Story = {
args: {
book: 8,
disabled: true
}
};

0 comments on commit 7224510

Please sign in to comment.