@rdlabo/ionic-angular-photo-editor 21.0.0 → 21.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +7 -0
- package/ng-package.json +7 -0
- package/package.json +3 -14
- package/src/lib/dictionaries.ts +31 -0
- package/src/lib/filter-preset.ts +44 -0
- package/src/lib/ion-components.ts +3 -0
- package/src/lib/pages/core.scss +63 -0
- package/src/lib/pages/photo-editor/photo-editor.page.html +95 -0
- package/src/lib/pages/photo-editor/photo-editor.page.scss +95 -0
- package/src/lib/pages/photo-editor/photo-editor.page.spec.ts +21 -0
- package/src/lib/pages/photo-editor/photo-editor.page.ts +221 -0
- package/src/lib/pages/photo-viewer/photo-viewer.page.html +31 -0
- package/src/lib/pages/photo-viewer/photo-viewer.page.scss +29 -0
- package/src/lib/pages/photo-viewer/photo-viewer.page.spec.ts +25 -0
- package/src/lib/pages/photo-viewer/photo-viewer.page.ts +134 -0
- package/src/lib/pages/util.ts +48 -0
- package/src/lib/photoEditorErrors.ts +5 -0
- package/src/lib/services/photo-file.service.spec.ts +19 -0
- package/src/lib/services/photo-file.service.ts +161 -0
- package/src/lib/types.ts +55 -0
- package/src/public-api.ts +8 -0
- package/tsconfig.lib.json +14 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +14 -0
- package/fesm2022/rdlabo-ionic-angular-photo-editor.mjs +0 -523
- package/fesm2022/rdlabo-ionic-angular-photo-editor.mjs.map +0 -1
- package/types/rdlabo-ionic-angular-photo-editor.d.ts +0 -132
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
ion-button.viewer-close-button {
|
|
2
|
+
--color: var(--editor-color) !important;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
img.circle {
|
|
6
|
+
border-radius: 50%;
|
|
7
|
+
display: block;
|
|
8
|
+
padding: 16px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
swiper-container {
|
|
12
|
+
height: 100%;
|
|
13
|
+
max-height: 100%;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
swiper-slide {
|
|
17
|
+
height: 100%;
|
|
18
|
+
max-height: 100%;
|
|
19
|
+
display: flex;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
align-items: center;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
swiper-slide img {
|
|
25
|
+
display: block;
|
|
26
|
+
object-fit: contain;
|
|
27
|
+
max-width: 100%;
|
|
28
|
+
max-height: 100%;
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { ComponentRef } from '@angular/core';
|
|
3
|
+
import { PhotoViewerPage } from './photo-viewer.page';
|
|
4
|
+
import { testConfig } from '../../../../../util/test.config';
|
|
5
|
+
|
|
6
|
+
describe('PhotoViewerPage', () => {
|
|
7
|
+
let component: PhotoViewerPage;
|
|
8
|
+
let fixture: ComponentFixture<PhotoViewerPage>;
|
|
9
|
+
let componentRef: ComponentRef<PhotoViewerPage>;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
TestBed.configureTestingModule({
|
|
13
|
+
providers: testConfig.providers,
|
|
14
|
+
});
|
|
15
|
+
fixture = TestBed.createComponent(PhotoViewerPage);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
componentRef = fixture.componentRef;
|
|
18
|
+
componentRef.setInput('imageUrls', []);
|
|
19
|
+
fixture.detectChanges();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should create', () => {
|
|
23
|
+
expect(component).toBeTruthy();
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
CUSTOM_ELEMENTS_SCHEMA,
|
|
5
|
+
effect,
|
|
6
|
+
ElementRef,
|
|
7
|
+
inject,
|
|
8
|
+
input,
|
|
9
|
+
OnDestroy,
|
|
10
|
+
OnInit,
|
|
11
|
+
signal,
|
|
12
|
+
viewChild,
|
|
13
|
+
} from '@angular/core';
|
|
14
|
+
import { IonicSlides, ModalController } from '@ionic/angular/standalone';
|
|
15
|
+
import { Navigation, Zoom } from 'swiper/modules';
|
|
16
|
+
import { fromEvent, Subscription, throttleTime, withLatestFrom, zipWith } from 'rxjs';
|
|
17
|
+
import { SwiperContainer } from 'swiper/element';
|
|
18
|
+
import { ionComponents } from '../../ion-components';
|
|
19
|
+
import { IDictionaryForViewer, IPhotoViewerDismiss } from '../../types';
|
|
20
|
+
import { register } from 'swiper/element/bundle';
|
|
21
|
+
import { dictionaryForViewer } from '../../dictionaries';
|
|
22
|
+
import { BooleanInput, coerceBooleanProperty, coerceNumberProperty, NumberInput } from '@angular/cdk/coercion';
|
|
23
|
+
import { initializeViewerIcons, waitToFindDom } from '../util';
|
|
24
|
+
|
|
25
|
+
@Component({
|
|
26
|
+
selector: 'app-photo-image',
|
|
27
|
+
templateUrl: './photo-viewer.page.html',
|
|
28
|
+
styleUrls: ['../core.scss', './photo-viewer.page.scss'],
|
|
29
|
+
imports: [...ionComponents],
|
|
30
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
31
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
32
|
+
})
|
|
33
|
+
export class PhotoViewerPage implements OnInit, OnDestroy {
|
|
34
|
+
readonly imageUrls = input.required<string[]>();
|
|
35
|
+
readonly index = input<number, NumberInput>(0, {
|
|
36
|
+
transform: coerceNumberProperty,
|
|
37
|
+
});
|
|
38
|
+
readonly isCircle = input<boolean, BooleanInput>(false, {
|
|
39
|
+
transform: coerceBooleanProperty,
|
|
40
|
+
});
|
|
41
|
+
readonly enableDelete = input<boolean, BooleanInput>(false, {
|
|
42
|
+
transform: coerceBooleanProperty,
|
|
43
|
+
});
|
|
44
|
+
readonly enableFooterSafeArea = input<boolean, BooleanInput>(false, {
|
|
45
|
+
transform: coerceBooleanProperty,
|
|
46
|
+
});
|
|
47
|
+
readonly labels = input<Partial<IDictionaryForViewer>>();
|
|
48
|
+
readonly setLabels = effect(() => {
|
|
49
|
+
if (this.labels()) {
|
|
50
|
+
this.dictionary.update((value) => ({ ...value, ...this.labels() }));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
readonly swiper = viewChild.required<ElementRef<SwiperContainer>>('swiper');
|
|
55
|
+
protected readonly dictionary = signal<IDictionaryForViewer>(dictionaryForViewer());
|
|
56
|
+
|
|
57
|
+
readonly watchSwipe$ = new Subscription();
|
|
58
|
+
readonly modalCtrl = inject(ModalController);
|
|
59
|
+
private readonly el = inject(ElementRef);
|
|
60
|
+
|
|
61
|
+
constructor() {
|
|
62
|
+
register();
|
|
63
|
+
initializeViewerIcons();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async ngOnInit() {
|
|
67
|
+
waitToFindDom(this.el.nativeElement, 'swiper-container').then(() => {
|
|
68
|
+
const index = this.index();
|
|
69
|
+
const swiper = this.swiper();
|
|
70
|
+
Object.assign(swiper.nativeElement, {
|
|
71
|
+
modules: [Navigation, Zoom, IonicSlides],
|
|
72
|
+
initialSlide: index,
|
|
73
|
+
slidesPerView: 1,
|
|
74
|
+
pagination: {
|
|
75
|
+
enabled: true,
|
|
76
|
+
clickable: true,
|
|
77
|
+
},
|
|
78
|
+
zoom: true,
|
|
79
|
+
});
|
|
80
|
+
swiper.nativeElement.initialize();
|
|
81
|
+
swiper.nativeElement.swiper.zoom.enable();
|
|
82
|
+
|
|
83
|
+
swiper.nativeElement.swiper.activeIndex = index;
|
|
84
|
+
swiper.nativeElement.swiper.update();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
this.watchSwipe$.add(
|
|
88
|
+
fromEvent<TouchEvent>(this.el.nativeElement, 'touchstart')
|
|
89
|
+
.pipe(
|
|
90
|
+
zipWith(
|
|
91
|
+
fromEvent<TouchEvent>(this.el.nativeElement, 'touchend').pipe(
|
|
92
|
+
withLatestFrom(fromEvent<TouchEvent>(this.el.nativeElement, 'touchmove')),
|
|
93
|
+
),
|
|
94
|
+
),
|
|
95
|
+
throttleTime(1),
|
|
96
|
+
)
|
|
97
|
+
.subscribe(([touchstart, [_, touchmove]]) => {
|
|
98
|
+
const touchstartClientX = touchstart.touches ? touchstart.touches[0].clientX : (touchstart as any).detail[1].clientX;
|
|
99
|
+
const touchmoveClientX = touchmove.touches ? touchmove.touches[0].clientX : (touchmove as any).detail[1].clientX;
|
|
100
|
+
|
|
101
|
+
const touchstartClientY = touchstart.touches ? touchstart.touches[0].clientY : (touchstart as any).detail[1].clientY;
|
|
102
|
+
const touchmoveClientY = touchmove.touches ? touchmove.touches[0].clientY : (touchmove as any).detail[1].clientY;
|
|
103
|
+
|
|
104
|
+
const xDiff = touchstartClientX - touchmoveClientX;
|
|
105
|
+
const yDiff = touchstartClientY - touchmoveClientY;
|
|
106
|
+
|
|
107
|
+
const slides = (this.swiper() as any).nativeElement.querySelectorAll('swiper-slide') as HTMLElement[];
|
|
108
|
+
const isZoomed = Array.from(slides).find((slide: HTMLElement) => {
|
|
109
|
+
return ['swiper-slide-zoomed', 'swiper-slide-active'].every((c) => slide.classList.contains(c));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const threshold = touchmove.touches ? -50 : -5;
|
|
113
|
+
|
|
114
|
+
if (!isZoomed && Math.abs(xDiff) < Math.abs(threshold) && yDiff < threshold && touchstart.timeStamp <= touchmove.timeStamp) {
|
|
115
|
+
this.watchSwipe$.unsubscribe();
|
|
116
|
+
this.modalCtrl.dismiss();
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
ngOnDestroy() {
|
|
123
|
+
this.watchSwipe$.unsubscribe();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
remove() {
|
|
127
|
+
this.modalCtrl.dismiss({
|
|
128
|
+
delete: {
|
|
129
|
+
index: this.swiper().nativeElement.swiper.activeIndex,
|
|
130
|
+
value: this.imageUrls()[this.swiper().nativeElement.swiper.activeIndex],
|
|
131
|
+
},
|
|
132
|
+
} as IPhotoViewerDismiss);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { addIcons } from 'ionicons';
|
|
2
|
+
import {
|
|
3
|
+
checkmarkOutline,
|
|
4
|
+
closeOutline,
|
|
5
|
+
colorFilterOutline,
|
|
6
|
+
cropOutline,
|
|
7
|
+
expandOutline,
|
|
8
|
+
refreshOutline,
|
|
9
|
+
removeOutline,
|
|
10
|
+
send,
|
|
11
|
+
squareOutline,
|
|
12
|
+
sunnyOutline,
|
|
13
|
+
tabletLandscapeOutline,
|
|
14
|
+
} from 'ionicons/icons';
|
|
15
|
+
|
|
16
|
+
export const initializeViewerIcons = (): void => {
|
|
17
|
+
addIcons({
|
|
18
|
+
closeOutline,
|
|
19
|
+
removeOutline,
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const initializeEditorIcons = (): void => {
|
|
24
|
+
addIcons({
|
|
25
|
+
closeOutline,
|
|
26
|
+
send,
|
|
27
|
+
cropOutline,
|
|
28
|
+
colorFilterOutline,
|
|
29
|
+
sunnyOutline,
|
|
30
|
+
expandOutline,
|
|
31
|
+
tabletLandscapeOutline,
|
|
32
|
+
squareOutline,
|
|
33
|
+
refreshOutline,
|
|
34
|
+
checkmarkOutline,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const waitToFindDom = (nativeElement: HTMLElement, selector: string): Promise<void> => {
|
|
39
|
+
return new Promise<void>((resolve) => {
|
|
40
|
+
const interval = setInterval(() => {
|
|
41
|
+
const find = nativeElement.querySelector(selector);
|
|
42
|
+
if (find) {
|
|
43
|
+
clearInterval(interval);
|
|
44
|
+
resolve();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { PhotoFileService } from './photo-file.service';
|
|
4
|
+
import { testConfig } from '../../../../util/test.config';
|
|
5
|
+
|
|
6
|
+
describe('PhotoService', () => {
|
|
7
|
+
let service: PhotoFileService;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
TestBed.configureTestingModule({
|
|
11
|
+
providers: [...testConfig.providers],
|
|
12
|
+
});
|
|
13
|
+
service = TestBed.inject(PhotoFileService);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should be created', () => {
|
|
17
|
+
expect(service).toBeTruthy();
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { inject, Injectable, signal } from '@angular/core';
|
|
2
|
+
import { ActionSheetController, Platform } from '@ionic/angular/standalone';
|
|
3
|
+
import { Camera, CameraResultType, CameraSource, ImageOptions } from '@capacitor/camera';
|
|
4
|
+
import ImageEditor from 'tui-image-editor';
|
|
5
|
+
import { PhotoEditorErrors } from '../photoEditorErrors';
|
|
6
|
+
import { dictionaryForService } from '../dictionaries';
|
|
7
|
+
import { IDictionaryForService } from '../types';
|
|
8
|
+
|
|
9
|
+
@Injectable({
|
|
10
|
+
providedIn: 'root',
|
|
11
|
+
})
|
|
12
|
+
export class PhotoFileService {
|
|
13
|
+
private readonly $photoMaxSize = signal<number>(1000);
|
|
14
|
+
private readonly actionSheetCtrl = inject(ActionSheetController);
|
|
15
|
+
private readonly platform = inject(Platform);
|
|
16
|
+
private readonly dictionary = signal<IDictionaryForService>(dictionaryForService());
|
|
17
|
+
|
|
18
|
+
set photoMaxSize(value: number) {
|
|
19
|
+
this.$photoMaxSize.set(value);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set labels(d: IDictionaryForService) {
|
|
23
|
+
this.dictionary.update((value) => ({ ...value, ...d }));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async loadPhoto(limit: number): Promise<string[]> {
|
|
27
|
+
/**
|
|
28
|
+
* Using Input for browser
|
|
29
|
+
*/
|
|
30
|
+
if (!this.platform.is('capacitor')) {
|
|
31
|
+
return this.getPictureFromBrowser();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const actionSheet = await this.actionSheetCtrl.create({
|
|
35
|
+
buttons: [
|
|
36
|
+
{
|
|
37
|
+
text: this.dictionary().camera,
|
|
38
|
+
handler: () => {
|
|
39
|
+
actionSheet.dismiss('camera');
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
text: this.dictionary().album,
|
|
44
|
+
handler: () => {
|
|
45
|
+
actionSheet.dismiss('album');
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
text: this.dictionary().cancel,
|
|
50
|
+
role: 'cancel',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
await actionSheet.present();
|
|
55
|
+
const { data } = await actionSheet.onDidDismiss<'camera' | 'album'>();
|
|
56
|
+
if (!data) {
|
|
57
|
+
return Promise.reject(PhotoEditorErrors.cancel);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (data === 'camera') {
|
|
61
|
+
const defaultCamera: ImageOptions = {
|
|
62
|
+
quality: 100,
|
|
63
|
+
width: this.$photoMaxSize(),
|
|
64
|
+
allowEditing: false,
|
|
65
|
+
resultType: CameraResultType.DataUrl,
|
|
66
|
+
source: CameraSource.Camera,
|
|
67
|
+
presentationStyle: 'popover',
|
|
68
|
+
};
|
|
69
|
+
const image = await Camera.getPhoto(defaultCamera).catch(() => undefined);
|
|
70
|
+
if (!image?.dataUrl) {
|
|
71
|
+
return Promise.reject(PhotoEditorErrors.cancel);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!image.dataUrl.includes('capacitor://localhost')) {
|
|
75
|
+
return [image.dataUrl];
|
|
76
|
+
}
|
|
77
|
+
return Promise.all([image.dataUrl].map(async (image) => await this.loadPhotoFromFilePath(image)));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (data === 'album') {
|
|
81
|
+
const images = await Camera.pickImages({
|
|
82
|
+
quality: 100,
|
|
83
|
+
width: this.$photoMaxSize(),
|
|
84
|
+
limit,
|
|
85
|
+
presentationStyle: 'popover',
|
|
86
|
+
}).catch(() => undefined);
|
|
87
|
+
|
|
88
|
+
if (!images) {
|
|
89
|
+
return Promise.reject(PhotoEditorErrors.cancel);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return Promise.all(images.photos.map(async (image) => await this.loadPhotoFromFilePath(image.webPath)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Not run on this line. This is for lint
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private getPictureFromBrowser(): Promise<string[]> {
|
|
100
|
+
const inputFile: HTMLInputElement | null = document.querySelector('input#browserPhotoUploader');
|
|
101
|
+
|
|
102
|
+
if (!inputFile) {
|
|
103
|
+
return Promise.reject(PhotoEditorErrors.initialize);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
const cancelMethod = () => {
|
|
108
|
+
inputFile!.removeEventListener('cancel', cancelMethod, false);
|
|
109
|
+
inputFile!.removeEventListener('change', changeMethod, false);
|
|
110
|
+
|
|
111
|
+
reject(PhotoEditorErrors.cancel);
|
|
112
|
+
};
|
|
113
|
+
const changeMethod = (e: Event) => {
|
|
114
|
+
inputFile!.removeEventListener('cancel', cancelMethod, false);
|
|
115
|
+
inputFile!.removeEventListener('change', changeMethod, false);
|
|
116
|
+
|
|
117
|
+
if (!(e.target as HTMLInputElement).files || !(e.target as HTMLInputElement).files![0]) {
|
|
118
|
+
reject(PhotoEditorErrors.cancel);
|
|
119
|
+
}
|
|
120
|
+
const file = (e.target as HTMLInputElement).files![0];
|
|
121
|
+
const reader = new FileReader();
|
|
122
|
+
|
|
123
|
+
reader.onload = (() => {
|
|
124
|
+
if (file.type.indexOf('image') < 0) {
|
|
125
|
+
reject(PhotoEditorErrors.type);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return async (event) => {
|
|
129
|
+
inputFile.value = '';
|
|
130
|
+
const result = event.target!.result as string;
|
|
131
|
+
const data = await this.loadPhotoFromFilePath(result);
|
|
132
|
+
resolve([data]);
|
|
133
|
+
};
|
|
134
|
+
})();
|
|
135
|
+
|
|
136
|
+
reader.readAsDataURL(file);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
inputFile!.addEventListener('cancel', cancelMethod, false);
|
|
140
|
+
inputFile!.addEventListener('change', changeMethod, false);
|
|
141
|
+
inputFile.click();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async loadPhotoFromFilePath(filePath: string): Promise<string> {
|
|
146
|
+
const defaultInstance = new ImageEditor(document.createElement('div'), {
|
|
147
|
+
cssMaxWidth: this.$photoMaxSize(),
|
|
148
|
+
cssMaxHeight: this.$photoMaxSize(),
|
|
149
|
+
});
|
|
150
|
+
const blob = await fetch(filePath).then((res) => res.blob());
|
|
151
|
+
const loaded = await defaultInstance.loadImageFromFile(new File([blob], 'data.png', { type: blob.type }));
|
|
152
|
+
|
|
153
|
+
const maxSize = Math.max(loaded.newWidth, loaded.newHeight);
|
|
154
|
+
const dataUrl = defaultInstance.toDataURL({
|
|
155
|
+
multiplier: this.$photoMaxSize() / maxSize,
|
|
156
|
+
});
|
|
157
|
+
defaultInstance.destroy();
|
|
158
|
+
|
|
159
|
+
return dataUrl;
|
|
160
|
+
}
|
|
161
|
+
}
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface IFilter {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
option: any;
|
|
5
|
+
data: string;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ISize {
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IDictionaryForEditor {
|
|
16
|
+
save: string;
|
|
17
|
+
crop: string;
|
|
18
|
+
filter: string;
|
|
19
|
+
brightness: string;
|
|
20
|
+
original: string;
|
|
21
|
+
invert: string;
|
|
22
|
+
sepia: string;
|
|
23
|
+
vintage: string;
|
|
24
|
+
blur: string;
|
|
25
|
+
grayscale: string;
|
|
26
|
+
sharpen: string;
|
|
27
|
+
emboss: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface IFilterPreset {
|
|
31
|
+
name: string;
|
|
32
|
+
type: string;
|
|
33
|
+
option: any;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IPhotoEditorDismiss {
|
|
37
|
+
value: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface IPhotoViewerDismiss {
|
|
41
|
+
delete: {
|
|
42
|
+
index: number;
|
|
43
|
+
value: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IDictionaryForViewer {
|
|
48
|
+
delete: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface IDictionaryForService {
|
|
52
|
+
camera: string;
|
|
53
|
+
album: string;
|
|
54
|
+
cancel: string;
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/lib",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"inlineSources": true,
|
|
9
|
+
"types": []
|
|
10
|
+
},
|
|
11
|
+
"exclude": [
|
|
12
|
+
"**/*.spec.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
|
2
|
+
{
|
|
3
|
+
"extends": "../../tsconfig.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"outDir": "../../out-tsc/spec",
|
|
6
|
+
"types": [
|
|
7
|
+
"jasmine"
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
"include": [
|
|
11
|
+
"**/*.spec.ts",
|
|
12
|
+
"**/*.d.ts"
|
|
13
|
+
]
|
|
14
|
+
}
|