Skip to content

Commit 1bff868

Browse files
committed
Code viewer invert colors feature
* add invert colors in code viewer * card-scanner with mat-dialog-* components
1 parent 524aa4a commit 1bff868

File tree

7 files changed

+113
-71
lines changed

7 files changed

+113
-71
lines changed

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cardholder-pwa",
3-
"version": "0.0.6",
3+
"version": "0.0.7",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve --port=4207 --open",

frontend/public/i18n/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"SAVE": "Card saved",
7070
"DELETE": "Card deleted"
7171
}
72+
},
73+
"VIEWER": {
74+
"INVERT": "Invert colors"
7275
}
7376
},
7477
"USER": {

frontend/public/i18n/ru.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@
6969
"SAVE": "Карта сохранена",
7070
"DELETE": "Карта удалена"
7171
}
72+
},
73+
"VIEWER": {
74+
"INVERT": "Инвертировать цвета"
7275
}
7376
},
7477
"USER": {
Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
1-
<div class="card-scanner">
1+
<mat-dialog-content class="mat-dialog-content">
22
<video
3-
class="card-scanner-video"
3+
class="mat-dialog-content-video"
44
#video>
55
</video>
6-
<section class="card-scanner-footer">
7-
<button
8-
mat-button
9-
(click)="onClickSelectDevice()">
10-
<mat-icon>videocam_outlined</mat-icon>
11-
@if (deviceControl.value) {
12-
{{ deviceControl.value.label }}
13-
} @else {
14-
{{ 'CARDS.CARD.SCAN.SELECT_SOURCE' | translate }}
15-
}
16-
</button>
17-
<button
18-
mat-button
19-
class="accent-color"
20-
(click)="fileInput.click()">
21-
<mat-icon>image</mat-icon>
22-
{{ 'CARDS.CARD.SCAN.FROM_FILE' | translate }}
23-
<input
24-
#fileInput
25-
hidden
26-
type="file"
27-
id="card-scanner-from-file"
28-
accept="image/*"
29-
multiple="false"
30-
(change)="decodeFromFile($any($event))" />
31-
</button>
32-
<button
33-
mat-button
34-
(click)="close()">
35-
{{ 'GENERAL.CLOSE' | translate }}
36-
</button>
37-
</section>
38-
</div>
6+
</mat-dialog-content>
7+
<mat-dialog-actions class="mat-dialog-actions">
8+
<button
9+
mat-button
10+
(click)="onClickSelectDevice()">
11+
<mat-icon>videocam_outlined</mat-icon>
12+
@if (deviceControl.value) {
13+
{{ deviceControl.value.label }}
14+
} @else {
15+
{{ 'CARDS.CARD.SCAN.SELECT_SOURCE' | translate }}
16+
}
17+
</button>
18+
<button
19+
mat-button
20+
class="accent-color"
21+
(click)="fileInput.click()">
22+
<mat-icon>image</mat-icon>
23+
{{ 'CARDS.CARD.SCAN.FROM_FILE' | translate }}
24+
<input
25+
#fileInput
26+
hidden
27+
type="file"
28+
id="card-scanner-from-file"
29+
accept="image/*"
30+
multiple="false"
31+
(change)="decodeFromFile($any($event))" />
32+
</button>
33+
<button
34+
mat-flat-button
35+
(click)="close()">
36+
{{ 'GENERAL.CLOSE' | translate }}
37+
</button>
38+
</mat-dialog-actions>
Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
1-
.card-scanner {
2-
width: 100%;
3-
height: 100%;
4-
display: flex;
5-
flex-direction: column;
6-
gap: 16px;
1+
:host {
2+
.mat-dialog-content {
3+
overflow: hidden;
4+
padding: 0;
75

8-
&-video {
9-
width: 100%;
10-
flex: 1;
6+
&-video {
7+
width: 100%;
8+
height: 100%;
9+
}
1110
}
12-
&-footer {
13-
width: 100%;
14-
padding: 16px;
15-
display: flex;
16-
flex-direction: column;
17-
align-items: flex-end;
11+
12+
.mat-dialog-actions {
13+
margin-top: auto;
1814
gap: 8px;
1915
}
2016
}

frontend/src/app/features/card-scanner/card-scanner.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ import {
2929
} from 'rxjs';
3030
import { TranslateModule } from '@ngx-translate/core';
3131
import { FormControl, ReactiveFormsModule } from '@angular/forms';
32-
import { MatDialogRef } from '@angular/material/dialog';
32+
import {
33+
MatDialogActions,
34+
MatDialogContent,
35+
MatDialogRef,
36+
} from '@angular/material/dialog';
3337
import { MatIcon } from '@angular/material/icon';
3438
import {
3539
MatListItem,
@@ -82,6 +86,8 @@ export class CardScannerDeviceSheetComponent {
8286
MatButton,
8387
MatIcon,
8488
MatButton,
89+
MatDialogActions,
90+
MatDialogContent,
8591
],
8692
templateUrl: './card-scanner.component.html',
8793
styleUrl: './card-scanner.component.scss',

frontend/src/app/shared/components/card-code-viewer/card-code-viewer.component.ts

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
MatDialogRef,
1919
MatDialogTitle,
2020
} from '@angular/material/dialog';
21+
import { MatIcon } from '@angular/material/icon';
2122
import Bwip from '@bwip-js/browser';
2223
import { TranslateModule } from '@ngx-translate/core';
2324
import type { BarcodeFormat } from '@zxing/library';
@@ -36,7 +37,8 @@ export interface ICardCodeViewerData {
3637
scale: number;
3738
/**
3839
* Color of barcode.
39-
* @default value of var(--mat-sys-on-background)
40+
* It should be valid css color value (not variable).
41+
* @default value of var(--mat-sys-on-surface)
4042
*/
4143
color: string;
4244
}
@@ -52,16 +54,15 @@ export class CardCodeViewerComponent
5254
implements OnInit, OnChanges, ICardCodeViewerData
5355
{
5456
protected readonly matDialog = inject(MatDialog);
57+
5558
@Input() card: Partial<ICardBase> = null;
5659
@Input() scale: number = 3;
5760
@Input('color') set _color(value: string) {
5861
if (value) {
5962
this.color = value;
6063
}
6164
}
62-
color: string = getComputedStyle(document.documentElement).getPropertyValue(
63-
'--mat-sys-on-background'
64-
);
65+
color: string = this.getCssVariableValue('--mat-sys-on-surface');
6566
/**
6667
* Barcode canvas element ref
6768
*/
@@ -70,17 +71,23 @@ export class CardCodeViewerComponent
7071

7172
ngOnInit(): void {
7273
if (this.card) {
73-
this.tryDrawCode(this.card.code, this.card.code_type);
74+
this.tryDrawCode(this.card.code, this.card.code_type, this.color);
7475
}
7576
}
7677

7778
ngOnChanges(changes: SimpleChanges): void {
7879
if (this.card) {
79-
this.tryDrawCode(this.card.code, this.card.code_type);
80+
this.tryDrawCode(this.card.code, this.card.code_type, this.color);
8081
}
8182
}
8283

83-
protected tryDrawCode(text: string, format: string): void {
84+
protected getCssVariableValue(variable: string): string {
85+
return getComputedStyle(document.documentElement).getPropertyValue(
86+
variable
87+
);
88+
}
89+
90+
protected tryDrawCode(text: string, format: string, color: string): void {
8491
const canvas = this.canvasRef.nativeElement;
8592
try {
8693
const bcid = ZxingToBwipMap[format as keyof typeof BarcodeFormat];
@@ -89,8 +96,8 @@ export class CardCodeViewerComponent
8996
text,
9097
scale: 3,
9198
includetext: true,
92-
textcolor: this.color,
93-
barcolor: this.color,
99+
textcolor: color,
100+
barcolor: color,
94101
});
95102
} catch (e) {
96103
const ctx = canvas.getContext('2d');
@@ -99,8 +106,11 @@ export class CardCodeViewerComponent
99106
}
100107
}
101108

102-
public viewInDialog() {
103-
this.matDialog.open(CardCodeViewerDialogComponent, {
109+
public viewInDialog(): MatDialogRef<
110+
CardCodeViewerDialogComponent,
111+
ICardCodeViewerData
112+
> {
113+
return this.matDialog.open(CardCodeViewerDialogComponent, {
104114
width: 'calc(100% - 50px)',
105115
height: 'calc(100% - 50px)',
106116
data: <ICardCodeViewerData>{
@@ -116,44 +126,68 @@ export class CardCodeViewerComponent
116126
standalone: true,
117127
imports: [
118128
MatButton,
129+
MatIcon,
119130
MatDialogActions,
120131
MatDialogContent,
121132
MatDialogTitle,
122133
TranslateModule,
123134
],
124135
template: `
125136
<h2 mat-dialog-title>{{ card.name }}</h2>
126-
<mat-dialog-content>
137+
<mat-dialog-content
138+
class="mat-dialog-content"
139+
[class.invert]="invert">
127140
<canvas
128141
class="canvas"
129-
#canvas>
142+
#canvas
143+
(click)="invert = !invert">
130144
</canvas>
131145
</mat-dialog-content>
132-
<mat-dialog-actions [style.margin-top]="'auto'">
146+
<mat-dialog-actions class="mat-dialog-actions">
147+
<button mat-button
148+
(click)="invert = !invert">
149+
<mat-icon>touch_app</mat-icon>
150+
{{ 'CARDS.VIEWER.INVERT' | translate }}
151+
</button>
133152
<button
134-
mat-button
153+
mat-flat-button
135154
(click)="close()">
136155
{{ 'GENERAL.CLOSE' | translate }}
137156
</button>
138157
</mat-dialog-actions>
139158
`,
140159
styleUrl: './card-code-viewer.component.scss',
141160
styles: `
142-
mat-dialog-content {
143-
display: flex;
144-
align-items: center;
145-
justify-content: center;
161+
:host {
162+
.mat-dialog-content {
163+
display: flex;
164+
align-items: center;
165+
justify-content: center;
166+
background-color: var(--mat-sys-surface);
167+
168+
&.invert {
169+
filter: invert(1);
170+
}
171+
}
172+
.mat-dialog-actions {
173+
margin-top: auto;
174+
gap: 8px;
175+
}
146176
}
147177
`,
148178
changeDetection: ChangeDetectionStrategy.OnPush,
149179
})
150180
export class CardCodeViewerDialogComponent extends CardCodeViewerComponent {
151181
private readonly matDialogRef = inject(MatDialogRef);
152182
private readonly data: ICardCodeViewerData = inject(MAT_DIALOG_DATA);
183+
184+
public invert: boolean = false;
185+
153186
constructor() {
154187
super();
155188
Object.assign(this, this.data);
156189
}
190+
157191
public close() {
158192
this.matDialogRef.close();
159193
}

0 commit comments

Comments
 (0)