Skip to content

Commit

Permalink
Merge pull request #270 from e-picsa/feat-custom-drawing
Browse files Browse the repository at this point in the history
feat(budget-tool): custom card drawing input
  • Loading branch information
chrismclarke authored Jun 5, 2024
2 parents 631fb2b + b6550c7 commit 582a8cf
Show file tree
Hide file tree
Showing 18 changed files with 373 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { PicsaCommonComponentsModule } from '@picsa/components';
import { PicsaDialogsModule } from '@picsa/shared/features';
import { PicsaDialogsModule, PicsaDrawingComponent } from '@picsa/shared/features';
import { PicsaDbModule } from '@picsa/shared/modules';
import { MobxAngularModule } from 'mobx-angular';

Expand Down Expand Up @@ -71,6 +71,7 @@ const components = [
RouterModule,
MatTooltipModule,
MatIconModule,
PicsaDrawingComponent,
],
exports: components,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,15 @@ export class BudgetCardImageComponent implements OnInit, OnDestroy {
// so convert to html that embeds within an <img> tag and div innerhtml
private convertSVGToImageData(svgTag: string) {
const encodedSVG = this._encodeSVG(svgTag);
const Html = `<img class='card-image' style='width:100%;height:100%;' src="data:image/svg+xml,${encodedSVG}"/>`;
const Html = `<img class='card-image' style='width:100%;height:100%;' src="${encodedSVG}"/>`;
return this.sanitizer.bypassSecurityTrustHtml(Html);
}

// method taken from http://yoksel.github.io/url-encoder/
// applies selective replacement of uri characters
private _encodeSVG(data: string): string {
const symbols = /[\r\n%#()<>?[\\\]^`{|}]/g;
data = data.replace(/"/g, "'");
data = data.replace(/>\s{1,}</g, '><');
data = data.replace(/\s{2,}/g, ' ');
return data.replace(symbols, encodeURIComponent);
// ignore already encoded
if (data.startsWith('data:')) return data;
return `data:image/svg+xml;base64,${btoa(data)}`;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
<mat-card appearance="outlined" mat-elevation-z4 class="budget-card selectable">
<!-- <mat-card appearance="outlined" mat-elevation-z4 class="budget-card selectable">
<budget-card-image [card]="card"></budget-card-image>
<div class="card-title">{{ card.label | translate }}</div>
</mat-card>
<mat-form-field>
</mat-card> -->

<picsa-custom-drawing (onChange)="setBudgetDrawing($event)"></picsa-custom-drawing>
<mat-form-field class="label-input">
<mat-label>{{'Card Label' | translate}}</mat-label>
<input matInput [(ngModel)]="card.label" />
</mat-form-field>
<button mat-stroked-button (click)="save()" [disabled]="card.label === ''" style="margin-top: 1em; width: 70%">
<button mat-stroked-button (click)="save()" [disabled]="!card.label || !imgData" style="margin-top: 1em; width: 70%">
<mat-icon>check</mat-icon>{{ 'Save' | translate }}
</button>
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@
background: #dcdcdc;
overflow: auto;
}
.label-input {
width: 100%;
margin-top: 1rem;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@ import { IBudgetCard } from '../../../schema';
templateUrl: './card-new-dialog.html',
styleUrls: ['./card-new-dialog.scss'],
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
export class BudgetCardNewDialog {
public card: IBudgetCard;
constructor(public dialogRef: MatDialogRef<BudgetCardNewDialog>, @Inject(MAT_DIALOG_DATA) card: IBudgetCard) {
this.card = card;
}

public imgData: string;

public setBudgetDrawing(svgData: string) {
if (svgData) {
this.imgData = `data:image/svg+xml;base64,${btoa(svgData)}`;
}
}
save() {
this.card.id = this.card.label.replace(/\s+/g, '-').toLowerCase();
this.card.id = `custom_${this.card.label.replace(/\s+/g, '-').toLowerCase()}`;
this.card.customMeta = {
imgData: this.generateImage(this.card.label),
imgData: this.imgData,
dateCreated: new Date().toISOString(),
createdBy: '',
};
Expand All @@ -26,7 +35,7 @@ export class BudgetCardNewDialog {

// return an svg circle with text in the middle
// text is either first 2 initials (if multiple words) or first 2 letters (if one word)
generateImage(text: string) {
private generateImage(text: string) {
const byWord = text.split(' ');
const abbr = byWord.length > 1 ? `${byWord[0].charAt(0)}.${byWord[1].charAt(0)}` : text.substring(0, 2);
return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export class BudgetCardNew {
imgType: 'svg',
};
const dialogRef = this.dialog.open(BudgetCardNewDialog, {
width: '250px',
data: card,
});

Expand Down
13 changes: 7 additions & 6 deletions libs/shared/src/features/data-table/data-table.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataTableComponent } from './data-table.component';

describe('DataTableComponent', () => {
let component: DataTableComponent;
let fixture: ComponentFixture<DataTableComponent>;
import { PicsaDataTableComponent } from './data-table.component';

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

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

fixture = TestBed.createComponent(DataTableComponent);
fixture = TestBed.createComponent(PicsaDataTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down
39 changes: 39 additions & 0 deletions libs/shared/src/features/drawing/drawing.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<div class="dialog-container">
<div class="edit-buttons">
<button mat-stroked-button (click)="undoSvgStroke()"><mat-icon>undo</mat-icon>{{ 'Undo' | translate }}</button>
<button mat-stroked-button (click)="redoSvgStroke()"><mat-icon>redo</mat-icon>{{ 'Redo' | translate }}</button>
<button mat-stroked-button style="margin-left: auto" (click)="clearDraw()">{{ 'Clear All' | translate }}</button>
</div>
<div class="svg-container" [style.width]="size() + 'px'" [style.height]="size() + 'px'">
<svg
xmlns="http://www.w3.org/2000/svg"
[attr.viewBox]="viewbox()"
[attr.height]="size()"
[attr.width]="size()"
#svgElement
(pointerdown)="handlePointerDown($event)"
(pointermove)="handlePointerMove($event)"
(mouseup)="handlePointerUp()"
(pointerup)="handlePointerUp()"
style="touch-action: none"
>
<!-- Previously rendered segments -->
@for(segment of segments; track segment.id){
<path [attr.d]="segment.path()" [id]="segment.id" />
}

<!-- Active segment -->
@if(activeSegment.path()){
<path [attr.d]="activeSegment.path()" [id]="activeSegment.id" />
}
</svg>
<!-- Draw Prompt Logo -->
@if(segments.length===0 && !activeSegment.path()){
<div class="draw-logo">
<mat-icon>draw</mat-icon>
<div>{{ 'Draw Here' | translate }}</div>
</div>

}
</div>
</div>
28 changes: 28 additions & 0 deletions libs/shared/src/features/drawing/drawing.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.dialog-container {
text-align: center;
}
.svg-container {
border: 1px solid #eeeeee;
border-radius: 4px;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
.draw-logo {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: var(--color-light);
mat-icon {
font-size: 48px;
height: 48px;
width: 48px;
}
}

.edit-buttons {
display: flex;
gap: 8px;
margin-bottom: 8px;
}
22 changes: 22 additions & 0 deletions libs/shared/src/features/drawing/drawing.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { PicsaDrawingComponent } from './drawing.component';

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

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

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

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit 582a8cf

Please sign in to comment.