@kris701/ez-ui 0.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/README.md +64 -0
- package/ng-package.json +7 -0
- package/package.json +18 -0
- package/src/components/dateinput.ts +58 -0
- package/src/components/floatfileupload.ts +52 -0
- package/src/components/floattable/filters/tuiThDateFilter.ts +159 -0
- package/src/components/floattable/filters/tuiThSelectFilter.ts +178 -0
- package/src/components/floattable/filters/tuiThTextFilter.ts +161 -0
- package/src/components/floattable/floattable.presets.ts +197 -0
- package/src/components/floattable/floattable.ts +309 -0
- package/src/components/floattable/helpers/floattable.filterhelpers.ts +23 -0
- package/src/components/floattable/models/FloatTableFilter.ts +5 -0
- package/src/components/floattable/models/FloatTableFilterMethod.ts +5 -0
- package/src/components/floattable/models/FloatTableSort.ts +4 -0
- package/src/components/floattable/models/FloatTableSortFilterPreset.ts +10 -0
- package/src/components/floattable/models/FloatTableSortFilterPresetSave.ts +6 -0
- package/src/components/floattable/models/FloatTableSortMethod.ts +4 -0
- package/src/components/floattable/services/floattable.filterservice.ts +110 -0
- package/src/components/floattable/sorts/tuiThSortable.ts +75 -0
- package/src/components/markdowneditor.ts +340 -0
- package/src/components/menubar.ts +144 -0
- package/src/components/multiselect.ts +77 -0
- package/src/components/numberinput.ts +45 -0
- package/src/components/passwordinput.ts +44 -0
- package/src/components/textinput.ts +40 -0
- package/src/helpers/compressImage.ts +20 -0
- package/src/interfaces/baseCRUDInterface.ts +181 -0
- package/src/interfaces/baseListService.ts +62 -0
- package/src/public-api.ts +15 -0
- package/tsconfig.lib.json +12 -0
- package/tsconfig.lib.prod.json +11 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { TuiIcon, TuiInput } from '@taiga-ui/core';
|
|
5
|
+
import { TuiPassword } from '@taiga-ui/kit';
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: 'ezui-passwordinput',
|
|
9
|
+
imports: [
|
|
10
|
+
FormsModule,
|
|
11
|
+
CommonModule,
|
|
12
|
+
TuiInput,
|
|
13
|
+
TuiIcon,
|
|
14
|
+
TuiPassword
|
|
15
|
+
],
|
|
16
|
+
template: `
|
|
17
|
+
<tui-textfield [tuiTextfieldSize]="size" [iconStart]="icon">
|
|
18
|
+
@if(label){
|
|
19
|
+
<label tuiLabel>{{label}}</label>
|
|
20
|
+
}
|
|
21
|
+
<input tuiInput type="password" [(ngModel)]="value" (ngModelChange)="valueChange.emit(value)" [disabled]="disabled"/>
|
|
22
|
+
<tui-icon tuiPassword />
|
|
23
|
+
</tui-textfield>
|
|
24
|
+
`,
|
|
25
|
+
styles: `
|
|
26
|
+
`
|
|
27
|
+
})
|
|
28
|
+
export class EzUIPasswordInput implements OnChanges {
|
|
29
|
+
@Input() icon: string = '';
|
|
30
|
+
@Input() label: string = '';
|
|
31
|
+
|
|
32
|
+
@Input() size: "l" | "m" | "s" = 'm';
|
|
33
|
+
|
|
34
|
+
@Input() disabled: boolean = false;
|
|
35
|
+
|
|
36
|
+
@Input() value: string = "";
|
|
37
|
+
@Output() valueChange = new EventEmitter<string>();
|
|
38
|
+
|
|
39
|
+
ngOnChanges(changes: SimpleChanges) {
|
|
40
|
+
if (changes['value'] && changes['value'].currentValue != changes['value'].previousValue) {
|
|
41
|
+
this.value = changes['value'].currentValue;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common';
|
|
2
|
+
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { TuiInput } from '@taiga-ui/core';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'ezui-textinput',
|
|
8
|
+
imports: [
|
|
9
|
+
FormsModule,
|
|
10
|
+
CommonModule,
|
|
11
|
+
TuiInput
|
|
12
|
+
],
|
|
13
|
+
template: `
|
|
14
|
+
<tui-textfield [tuiTextfieldSize]="size" [iconStart]="icon">
|
|
15
|
+
@if(label){
|
|
16
|
+
<label tuiLabel>{{label}}</label>
|
|
17
|
+
}
|
|
18
|
+
<input tuiInput [(ngModel)]="value" (ngModelChange)="valueChange.emit(value)" [disabled]="disabled"/>
|
|
19
|
+
</tui-textfield>
|
|
20
|
+
`,
|
|
21
|
+
styles: `
|
|
22
|
+
`
|
|
23
|
+
})
|
|
24
|
+
export class EzUITextInput implements OnChanges {
|
|
25
|
+
@Input() icon: string = '';
|
|
26
|
+
@Input() label: string = '';
|
|
27
|
+
|
|
28
|
+
@Input() size: "l" | "m" | "s" = 'm';
|
|
29
|
+
|
|
30
|
+
@Input() disabled: boolean = false;
|
|
31
|
+
|
|
32
|
+
@Input() value: string = "";
|
|
33
|
+
@Output() valueChange = new EventEmitter<string>();
|
|
34
|
+
|
|
35
|
+
ngOnChanges(changes: SimpleChanges) {
|
|
36
|
+
if (changes['value'] && changes['value'].currentValue != changes['value'].previousValue) {
|
|
37
|
+
this.value = changes['value'].currentValue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Compressor from "compressorjs";
|
|
2
|
+
|
|
3
|
+
export const compressImage = async (
|
|
4
|
+
file: File,
|
|
5
|
+
quality: number,
|
|
6
|
+
maxHeight: number,
|
|
7
|
+
maxWidth: number,
|
|
8
|
+
convertSize?: number
|
|
9
|
+
): Promise<File | Blob> => {
|
|
10
|
+
return await new Promise((resolve, reject) => {
|
|
11
|
+
new Compressor(file, {
|
|
12
|
+
quality,
|
|
13
|
+
maxHeight,
|
|
14
|
+
maxWidth,
|
|
15
|
+
convertSize,
|
|
16
|
+
success: resolve,
|
|
17
|
+
error: reject,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { HttpClient } from '@angular/common/http';
|
|
2
|
+
import { Directive, signal } from '@angular/core';
|
|
3
|
+
import { TuiResponsiveDialogService } from '@taiga-ui/addon-mobile';
|
|
4
|
+
import { TuiNotificationService } from '@taiga-ui/core';
|
|
5
|
+
import { TUI_CONFIRM } from '@taiga-ui/kit';
|
|
6
|
+
import { firstValueFrom, switchMap } from 'rxjs';
|
|
7
|
+
|
|
8
|
+
@Directive()
|
|
9
|
+
export abstract class BaseCRUDInterface {
|
|
10
|
+
public isLoading = signal<boolean>(false);
|
|
11
|
+
|
|
12
|
+
public allItems = signal<any[]>([]);
|
|
13
|
+
public currentItem = signal<any>({} as any);
|
|
14
|
+
public newItemTemplate(): any {
|
|
15
|
+
return {} as any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public getAllEndpoint: string = '';
|
|
19
|
+
public getEndpoint: string = '';
|
|
20
|
+
public patchEndpoint: string = '';
|
|
21
|
+
public postEndpoint: string = '';
|
|
22
|
+
public deleteEndpoint: string = '';
|
|
23
|
+
|
|
24
|
+
public canRead: boolean = true;
|
|
25
|
+
public canWrite: boolean = true;
|
|
26
|
+
|
|
27
|
+
public showDialog = signal<boolean>(false);
|
|
28
|
+
|
|
29
|
+
public loadOnInit: boolean = true;
|
|
30
|
+
|
|
31
|
+
public showDeleteDialog: boolean = true;
|
|
32
|
+
public deleteDialogMessage : string = "Are you sure you want to delete this item?";
|
|
33
|
+
public showSaveDialog: boolean = false;
|
|
34
|
+
public savewDialogMessage : string = "Are you sure you want to save this item?";
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
public http: HttpClient,
|
|
38
|
+
public messageService: TuiNotificationService,
|
|
39
|
+
public confirmationService: TuiResponsiveDialogService
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
async ngOnInit() {
|
|
43
|
+
if (this.canRead && this.loadOnInit) await this.loadItems();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
public async loadItems() {
|
|
47
|
+
if (!this.canRead) throw new Error('You do not have read permissions!');
|
|
48
|
+
this.isLoading.set(true);
|
|
49
|
+
try {
|
|
50
|
+
this.allItems.set(await firstValueFrom(this.http.get<any[]>(this.getAllEndpoint)));
|
|
51
|
+
} catch {
|
|
52
|
+
this.messageService.open("Failed to load all items!", {
|
|
53
|
+
label: "Load Error",
|
|
54
|
+
appearance: 'negative',
|
|
55
|
+
icon:'circle-x',
|
|
56
|
+
autoClose: 10000
|
|
57
|
+
}).subscribe();
|
|
58
|
+
}
|
|
59
|
+
this.isLoading.set(false);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async saveItem() {
|
|
63
|
+
if (!this.canWrite) throw new Error('You do not have write permissions!');
|
|
64
|
+
|
|
65
|
+
if (this.showSaveDialog){
|
|
66
|
+
this.confirmationService.open<boolean>(
|
|
67
|
+
TUI_CONFIRM,
|
|
68
|
+
{
|
|
69
|
+
label: this.savewDialogMessage,
|
|
70
|
+
size: 's'
|
|
71
|
+
})
|
|
72
|
+
.pipe(switchMap(async (response) => {
|
|
73
|
+
if (response === true)
|
|
74
|
+
await this.saveItemInner();
|
|
75
|
+
}))
|
|
76
|
+
.subscribe();
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
await this.saveItemInner();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async saveItemInner(){
|
|
83
|
+
this.isLoading.set(true);
|
|
84
|
+
try {
|
|
85
|
+
var current = this.currentItem();
|
|
86
|
+
if (current.id != '') {
|
|
87
|
+
await firstValueFrom(this.http.patch(this.patchEndpoint, current));
|
|
88
|
+
this.messageService.open("The item was updated with the new values", {
|
|
89
|
+
label: "Item Updated",
|
|
90
|
+
appearance: 'positive',
|
|
91
|
+
icon:'circle-x',
|
|
92
|
+
autoClose: 1000
|
|
93
|
+
}).subscribe();
|
|
94
|
+
} else {
|
|
95
|
+
await firstValueFrom(this.http.post(this.postEndpoint, current));
|
|
96
|
+
this.messageService.open("A new items was created", {
|
|
97
|
+
label: "Item Created",
|
|
98
|
+
appearance: 'positive',
|
|
99
|
+
icon:'circle-x',
|
|
100
|
+
autoClose: 1000
|
|
101
|
+
}).subscribe();
|
|
102
|
+
}
|
|
103
|
+
this.showDialog.set(false);
|
|
104
|
+
await this.loadItems();
|
|
105
|
+
} catch {
|
|
106
|
+
this.messageService.open("Save failed!", {
|
|
107
|
+
label: "Save Error",
|
|
108
|
+
appearance: 'negative',
|
|
109
|
+
icon:'circle-x',
|
|
110
|
+
autoClose: 10000
|
|
111
|
+
}).subscribe();
|
|
112
|
+
}
|
|
113
|
+
this.isLoading.set(false);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async deleteItem() {
|
|
117
|
+
if (!this.canWrite) throw new Error('You do not have write permissions!');
|
|
118
|
+
|
|
119
|
+
if (this.showDeleteDialog){
|
|
120
|
+
this.confirmationService.open<boolean>(
|
|
121
|
+
TUI_CONFIRM,
|
|
122
|
+
{
|
|
123
|
+
label: this.deleteDialogMessage,
|
|
124
|
+
size: 's'
|
|
125
|
+
})
|
|
126
|
+
.pipe(switchMap(async (response) => {
|
|
127
|
+
if (response === true)
|
|
128
|
+
await this.deleteItemInner();
|
|
129
|
+
}))
|
|
130
|
+
.subscribe();
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
await this.deleteItemInner();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async deleteItemInner() {
|
|
137
|
+
this.isLoading.set(true);
|
|
138
|
+
try {
|
|
139
|
+
await firstValueFrom(this.http.delete(this.deleteEndpoint + '?ID=' + this.currentItem().id));
|
|
140
|
+
this.messageService.open("The item was successfully deleted", {
|
|
141
|
+
label: "Item Deleted",
|
|
142
|
+
appearance: 'positive',
|
|
143
|
+
icon:'circle-x',
|
|
144
|
+
autoClose: 1000
|
|
145
|
+
}).subscribe();
|
|
146
|
+
this.showDialog.set(false);
|
|
147
|
+
await this.loadItems();
|
|
148
|
+
} catch {
|
|
149
|
+
this.messageService.open("Delete failed!", {
|
|
150
|
+
label: "Delete Error",
|
|
151
|
+
appearance: 'negative',
|
|
152
|
+
icon:'circle-x',
|
|
153
|
+
autoClose: 10000
|
|
154
|
+
}).subscribe();
|
|
155
|
+
}
|
|
156
|
+
this.isLoading.set(false);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public async showEditItem(id: string) {
|
|
160
|
+
if (!this.canRead) throw new Error('You do not have read permissions!');
|
|
161
|
+
this.isLoading.set(true);
|
|
162
|
+
try {
|
|
163
|
+
var fetched = await firstValueFrom(this.http.get<any>(this.getEndpoint + '?ID=' + id));
|
|
164
|
+
this.currentItem.set(fetched);
|
|
165
|
+
this.showDialog.set(true);
|
|
166
|
+
} catch {
|
|
167
|
+
this.messageService.open("Loading failed!", {
|
|
168
|
+
label: "Load Error",
|
|
169
|
+
appearance: 'negative',
|
|
170
|
+
icon:'circle-x',
|
|
171
|
+
autoClose: 10000
|
|
172
|
+
}).subscribe();
|
|
173
|
+
}
|
|
174
|
+
this.isLoading.set(false);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
public async showAddItem() {
|
|
178
|
+
this.currentItem.set(this.newItemTemplate());
|
|
179
|
+
this.showDialog.set(true);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HttpClient } from "@angular/common/http";
|
|
2
|
+
import { Directive, EventEmitter, signal } from "@angular/core";
|
|
3
|
+
import { firstValueFrom } from "rxjs";
|
|
4
|
+
|
|
5
|
+
@Directive()
|
|
6
|
+
export class BaseListService<T,TList extends IIdentifiable> {
|
|
7
|
+
public items = signal<TList[]>([]);
|
|
8
|
+
|
|
9
|
+
public getAllEndpoint: string = '';
|
|
10
|
+
public getEndpoint: string = '';
|
|
11
|
+
|
|
12
|
+
isLoaded : boolean = false;
|
|
13
|
+
isLoading : boolean = false;
|
|
14
|
+
|
|
15
|
+
public onUpdated : EventEmitter<any> = new EventEmitter();
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
public http: HttpClient
|
|
19
|
+
) {
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public async Load(){
|
|
23
|
+
if (!this.isLoading){
|
|
24
|
+
this.isLoaded = false;
|
|
25
|
+
this.isLoading = true;
|
|
26
|
+
var value = await firstValueFrom(this.http.get<TList[]>(this.getAllEndpoint));
|
|
27
|
+
this.items.set(value);
|
|
28
|
+
this.isLoading = false;
|
|
29
|
+
this.isLoaded = true;
|
|
30
|
+
this.onUpdated.emit();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async List() : Promise<TList[]>{
|
|
35
|
+
if (!this.isLoaded)
|
|
36
|
+
await this.waitForValue(() => this.isLoaded === true);
|
|
37
|
+
return this.items();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async ListGet(id : string) : Promise<TList | null>{
|
|
41
|
+
if (!this.isLoaded)
|
|
42
|
+
await this.waitForValue(() => this.isLoaded === true);
|
|
43
|
+
var target = this.items().find(x => x.id == id);
|
|
44
|
+
if (target)
|
|
45
|
+
return target;
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
public async Get(id : string) : Promise<T> {
|
|
50
|
+
var item = await firstValueFrom(this.http.get<T>(this.getEndpoint + "?ID=" + id));
|
|
51
|
+
return item;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async waitForValue(test : any) {
|
|
55
|
+
const delayMs = 500;
|
|
56
|
+
while(!test()) await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface IIdentifiable {
|
|
61
|
+
id : string;
|
|
62
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Components
|
|
2
|
+
export * from './components/dateinput';
|
|
3
|
+
export * from './components/markdowneditor';
|
|
4
|
+
export * from './components/multiselect';
|
|
5
|
+
export * from './components/numberinput';
|
|
6
|
+
export * from './components/passwordinput';
|
|
7
|
+
export * from './components/textinput';
|
|
8
|
+
|
|
9
|
+
// Helpers
|
|
10
|
+
export * from './helpers/compressImage';
|
|
11
|
+
|
|
12
|
+
// Interfaces
|
|
13
|
+
export * from './interfaces/baseCRUDInterface';
|
|
14
|
+
export * from './interfaces/baseListService';
|
|
15
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
2
|
+
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
3
|
+
{
|
|
4
|
+
"extends": "../../tsconfig.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"types": []
|
|
9
|
+
},
|
|
10
|
+
"include": ["src/**/*.ts"],
|
|
11
|
+
"exclude": ["**/*.spec.ts"]
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
|
|
2
|
+
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
|
|
3
|
+
{
|
|
4
|
+
"extends": "./tsconfig.lib.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"declarationMap": false
|
|
7
|
+
},
|
|
8
|
+
"angularCompilerOptions": {
|
|
9
|
+
"compilationMode": "partial"
|
|
10
|
+
}
|
|
11
|
+
}
|