arrmatura 5.0.4 → 5.2.0
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/commons/auth/AuthService.ts +111 -0
- package/commons/auth/UserService.ts +28 -78
- package/commons/auth/index.ts +4 -3
- package/commons/auth/user.xml +18 -20
- package/commons/components/buttons.xml +6 -4
- package/commons/components/layouts.xml +8 -8
- package/commons/components/modal.xml +4 -4
- package/commons/components/navigation.xml +21 -7
- package/commons/components/searchbar.xml +36 -22
- package/commons/components/table.xml +25 -5
- package/commons/components/toasts.xml +10 -5
- package/commons/forms/FormController.ts +48 -34
- package/commons/forms/Item.ts +93 -29
- package/commons/forms/ItemCollectionController.ts +113 -0
- package/commons/forms/ItemController.ts +50 -21
- package/commons/forms/NewItemController.ts +46 -0
- package/commons/forms/SmartareaField.xml +3 -3
- package/commons/forms/SuggestionField.xml +3 -3
- package/commons/forms/fields.xml +28 -15
- package/commons/forms/form.xml +3 -3
- package/commons/forms/index.ts +4 -4
- package/commons/index.ts +2 -2
- package/commons/services/ApiClientService.ts +32 -0
- package/commons/services/GSheetsService.ts +92 -64
- package/commons/services/Invoke.ts +24 -8
- package/commons/services/JsonpService.ts +1 -1
- package/commons/services/MetadataService.ts +50 -0
- package/commons/services/NavigationService.ts +20 -1
- package/commons/services/Service.ts +22 -28
- package/commons/services/Storage.ts +55 -0
- package/commons/services/ToastService.ts +16 -4
- package/commons/services/index.ts +3 -4
- package/core/Arrmatura.ts +4 -9
- package/core/CNode.ts +1 -2
- package/core/Component.ts +29 -31
- package/core/cnodes/conditionals.ts +3 -2
- package/core/expression.ts +1 -1
- package/core/register.ts +1 -1
- package/core/utils.ts +1 -3
- package/core-web/attributes.ts +38 -10
- package/core-web/elements.ts +22 -4
- package/core-web/index.ts +14 -2
- package/core-web/reflow.ts +3 -1
- package/core-web/support.ts +27 -0
- package/docs/index.js +350 -66
- package/lib/{arrayGroupBy.ts → array/arrayGroupBy.ts} +1 -1
- package/lib/{arraySortBy.ts → array/arraySortBy.ts} +5 -5
- package/lib/{arrayToObject.ts → array/arrayToObject.ts} +0 -0
- package/lib/array/arrayish.ts +7 -0
- package/lib/array/index.ts +4 -0
- package/lib/crypto/index.ts +25 -0
- package/lib/{dateFormat.ts → date/index.ts} +61 -3
- package/lib/func/curry.ts +11 -0
- package/lib/{fx.ts → func/fx.ts} +32 -38
- package/lib/{noop.ts → func/identity.ts} +1 -3
- package/lib/{nextId.ts → func/nextId.ts} +0 -0
- package/lib/func/noop.ts +6 -0
- package/lib/geo/index.js +22 -0
- package/lib/index.ts +17 -19
- package/lib/{obj.ts → object/index.ts} +8 -3
- package/lib/{codus.ts → parsing/codus.ts} +1 -1
- package/lib/{predicat.ts → parsing/compiler.ts} +0 -0
- package/lib/{scalar.ts → parsing/scalarParse.ts} +0 -0
- package/lib/{urls.ts → parsing/urls.ts} +0 -0
- package/lib/{xml.ts → parsing/xml.ts} +3 -3
- package/lib/{l10n.ts → resources/l10n.ts} +2 -2
- package/lib/{resources.ru.ts → resources/resources.ru.ts} +0 -0
- package/lib/{resources.ts → resources/resources.ts} +0 -0
- package/lib/{str.ts → string/index.ts} +2 -20
- package/lib/{createListeners.ts → support/createListeners.ts} +0 -0
- package/package.json +7 -6
- package/commons/forms/ItemCollection.ts +0 -152
- package/commons/forms/MetadataService.ts +0 -45
- package/commons/pipes/index.ts +0 -21
- package/commons/pipes/showCounts.ts +0 -16
- package/commons/services/LocalStorage.ts +0 -46
- package/lib/consts.ts +0 -3
- package/lib/curry.ts +0 -8
- package/lib/dateOf.ts +0 -31
- package/lib/dateParseISO8601.ts +0 -27
- package/lib/guid.ts +0 -7
|
@@ -1,13 +1,21 @@
|
|
|
1
|
-
import { arrayGroupBy } from '
|
|
2
|
-
import { getPredicat } from '
|
|
3
|
-
import { strFormat } from '../../lib';
|
|
1
|
+
import { arrayGroupBy } from '../../lib';
|
|
2
|
+
import { strFormat, getPredicat } from '../../lib';
|
|
4
3
|
import { Service } from '../services/Service';
|
|
5
4
|
|
|
5
|
+
const getFieldsGroups = (fieldProps: Hash[], names?: StringHash): Obj[] => {
|
|
6
|
+
return Object.values(arrayGroupBy(fieldProps, 'group')).map((e) => ({
|
|
7
|
+
name: names?.[e.id] || e.id,
|
|
8
|
+
...e,
|
|
9
|
+
}));
|
|
10
|
+
};
|
|
11
|
+
|
|
6
12
|
export class FormController extends Service {
|
|
7
13
|
data: { [k: string]: unknown } = {};
|
|
8
14
|
meta: any;
|
|
9
15
|
error: any;
|
|
10
16
|
change: any;
|
|
17
|
+
tabs?: StringHash;
|
|
18
|
+
groups?: StringHash;
|
|
11
19
|
|
|
12
20
|
setData(d: Data) {
|
|
13
21
|
const data = { ...d };
|
|
@@ -15,14 +23,14 @@ export class FormController extends Service {
|
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
get fieldsGroups(): Obj[] {
|
|
18
|
-
return
|
|
26
|
+
return getFieldsGroups(this.fieldProps, this.groups);
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
get tabsInfo() {
|
|
22
30
|
const tabs = Object.values(arrayGroupBy(this.fieldProps, ({ tab = 'tab' }) => tab)).map(({ id, items }) => ({
|
|
23
31
|
id,
|
|
24
|
-
name: id,
|
|
25
|
-
items:
|
|
32
|
+
name: this.tabs?.[id] || id,
|
|
33
|
+
items: getFieldsGroups(items, this.groups),
|
|
26
34
|
}));
|
|
27
35
|
return { enabled: tabs.length > 1, initialTab: tabs?.[0]?.id, tabs };
|
|
28
36
|
}
|
|
@@ -35,39 +43,45 @@ export class FormController extends Service {
|
|
|
35
43
|
.map((s) => s.trim().split('='))
|
|
36
44
|
.map(([id, name = id]) => ({ id, name }));
|
|
37
45
|
}
|
|
46
|
+
|
|
47
|
+
if (!typeSpec.includes('.')) return this.res('enums.' + typeSpec) || [];
|
|
48
|
+
|
|
38
49
|
const [key, subid, subKey] = strFormat(typeSpec, data).split('.');
|
|
50
|
+
const enumItems = this.res('enums.' + key) || [];
|
|
39
51
|
if (subKey) {
|
|
40
|
-
return
|
|
52
|
+
return enumItems.find(({ id }) => id === subid)?.[subKey] || [];
|
|
41
53
|
}
|
|
42
|
-
return
|
|
54
|
+
return enumItems;
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
get fieldProps() {
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
return (
|
|
59
|
+
this.meta?.map?.((field: { [k: string]: string }) => {
|
|
60
|
+
const { id, visible, disabled = false, caption, name, typeSpec } = field;
|
|
61
|
+
const form = this;
|
|
62
|
+
const data = this.data;
|
|
63
|
+
const value = data[id];
|
|
64
|
+
return {
|
|
65
|
+
...field,
|
|
66
|
+
error: (form.error && form.error.fields && form.error.fields[id]) || null,
|
|
67
|
+
get disabled(): boolean {
|
|
68
|
+
return getPredicat(disabled)(data);
|
|
69
|
+
},
|
|
70
|
+
get visible(): boolean {
|
|
71
|
+
return getPredicat(visible || 'true')(data);
|
|
72
|
+
},
|
|
73
|
+
get options(): Data[] | Data | null {
|
|
74
|
+
return form.resolveEnum(typeSpec, data);
|
|
75
|
+
},
|
|
76
|
+
value: value === undefined ? null : value,
|
|
77
|
+
caption: caption || name,
|
|
78
|
+
data,
|
|
79
|
+
onChange: ({ value, key }: { value: unknown; key?: string }) => {
|
|
80
|
+
this.data = { ...this.data, [key || id]: value };
|
|
81
|
+
this.change?.(this.data);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}) || []
|
|
85
|
+
);
|
|
72
86
|
}
|
|
73
87
|
}
|
package/commons/forms/Item.ts
CHANGED
|
@@ -1,68 +1,132 @@
|
|
|
1
|
-
import { arraySortBy } from '
|
|
1
|
+
import { arraySortBy } from '../../lib';
|
|
2
2
|
|
|
3
|
-
export const analyzeDataByTags = (
|
|
3
|
+
export const analyzeDataByTags = (
|
|
4
|
+
data: Item[],
|
|
5
|
+
selection: Hash<boolean>,
|
|
6
|
+
initials = '',
|
|
7
|
+
tags: Tag[] = [],
|
|
8
|
+
labelsFields: string
|
|
9
|
+
) => {
|
|
4
10
|
const sel = Object.entries(selection)
|
|
5
11
|
.filter(([_, val]) => val)
|
|
6
12
|
.map(([key]) => key);
|
|
13
|
+
const level = sel.length;
|
|
14
|
+
if (labelsFields) {
|
|
15
|
+
const lFields = labelsFields.split(',');
|
|
16
|
+
const fields = lFields.slice(0, level);
|
|
17
|
+
const actualData = !level ? data : data.filter((e) => !fields.find((field, index) => e[field] != sel[index]));
|
|
18
|
+
const tagsHash: Hash<Tag> = {};
|
|
19
|
+
if (level) {
|
|
20
|
+
tagsHash['*'] = { id: sel[level - 1], name: sel.join(' ➡ '), count: 0, selected: level > 0 };
|
|
21
|
+
}
|
|
7
22
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
actualData.forEach((e) => {
|
|
24
|
+
const id = String(e[lFields[level]] || '');
|
|
25
|
+
if (!id) return;
|
|
26
|
+
const tag = tagsHash[id] || (tagsHash[id] = { id, name: id, count: 0, selected: selection[id] });
|
|
27
|
+
if (!tag.selected) {
|
|
28
|
+
tag.count++;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
tags.push(...Object.values(tagsHash));
|
|
33
|
+
// tags.sort((a, b) => (a.selected === b.selected ? (a.id > b.id ? 1 : -1) : !a.selected ? 1 : -1));
|
|
34
|
+
return actualData;
|
|
35
|
+
}
|
|
36
|
+
if (level || !initials) {
|
|
37
|
+
const actualData = data.filter((e) => sel.reduce((r, s) => r && e.getLabels().has(s), true));
|
|
38
|
+
const tagsHash: Hash<Tag> = {};
|
|
39
|
+
sel.forEach((id) => {
|
|
40
|
+
tagsHash[id] = { id, name: id, count: 0, selected: true };
|
|
41
|
+
});
|
|
42
|
+
actualData.forEach((e) => {
|
|
43
|
+
e.getLabels().forEach((id: string) => {
|
|
44
|
+
const tag =
|
|
45
|
+
tagsHash[id] || (tagsHash[id] = { id, name: id, count: 0, selected: selection[id], nonSelectable: true });
|
|
13
46
|
if (!tag.selected) {
|
|
47
|
+
tag.nonSelectable = false;
|
|
14
48
|
tag.count++;
|
|
15
49
|
}
|
|
16
50
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
tags.push(...Object.values(tagsHash));
|
|
54
|
+
tags.sort((a, b) => (a.selected === b.selected ? (a.id > b.id ? 1 : -1) : !a.selected ? 1 : -1));
|
|
55
|
+
return actualData;
|
|
20
56
|
} else {
|
|
21
|
-
|
|
57
|
+
tags.push(...initials.split(',').map((id) => ({ id, name: id, count: 0, selected: false })));
|
|
22
58
|
data.forEach((e: Item) => {
|
|
23
59
|
tags.forEach((tag) => {
|
|
24
|
-
if (e
|
|
60
|
+
if (e.getLabels().has(tag.id)) {
|
|
25
61
|
tag.count++;
|
|
26
62
|
}
|
|
27
63
|
});
|
|
28
64
|
});
|
|
29
|
-
return
|
|
65
|
+
return data;
|
|
30
66
|
}
|
|
31
67
|
};
|
|
32
68
|
|
|
33
69
|
export const getSuggestions = (data: Item[], kluq: string) => {
|
|
34
70
|
if (!kluq) return null;
|
|
35
71
|
|
|
36
|
-
const words = {};
|
|
72
|
+
const words: Hash = {};
|
|
37
73
|
data.forEach((e) =>
|
|
38
|
-
e.$searchData.replace(
|
|
74
|
+
e.$searchData.replace(/[a-z0-9а-яёўі]{3,}/g, (w) => {
|
|
39
75
|
words[w] = 1;
|
|
40
76
|
return '';
|
|
41
77
|
})
|
|
42
78
|
);
|
|
79
|
+
const lokey = kluq.toLowerCase();
|
|
43
80
|
return arraySortBy(
|
|
44
81
|
Object.keys(words)
|
|
45
|
-
.filter((e) => e.
|
|
82
|
+
.filter((e) => e.startsWith(lokey))
|
|
46
83
|
.map((s) => ({ name: s, id: s })),
|
|
47
84
|
'name'
|
|
48
85
|
);
|
|
49
86
|
};
|
|
87
|
+
export const applyNavInfo = (data: Item[]) => {
|
|
88
|
+
let prev: Item | null = null;
|
|
89
|
+
return !data
|
|
90
|
+
? null
|
|
91
|
+
: data.map((item: Item, index) => {
|
|
92
|
+
item.$navInfo = { prev, index, total: data.length };
|
|
93
|
+
if (prev?.$navInfo) {
|
|
94
|
+
prev.$navInfo.next = item;
|
|
95
|
+
}
|
|
96
|
+
prev = item;
|
|
97
|
+
return item;
|
|
98
|
+
});
|
|
99
|
+
};
|
|
50
100
|
|
|
51
|
-
export class Item {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return this.$$tags || (this.$$tags = new Set((this.status + ',' + this.type || '').split(',').filter(Boolean)));
|
|
101
|
+
export class Item implements Delta {
|
|
102
|
+
id?: string;
|
|
103
|
+
$row?: string;
|
|
104
|
+
$: Hash<any> = {};
|
|
105
|
+
$navInfo?: { index: number; total: number; prev?: Item | null; next?: Item };
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
|
|
108
|
+
constructor({ $, ...opts }: Delta = {}) {
|
|
109
|
+
Object.assign(this, opts);
|
|
61
110
|
}
|
|
62
|
-
|
|
63
|
-
|
|
111
|
+
getLabels(): Set<string> {
|
|
112
|
+
const key = 'labels';
|
|
113
|
+
return (
|
|
114
|
+
this.$[key] ||
|
|
115
|
+
(this.$[key] = new Set(
|
|
116
|
+
String(this[key] || this.labels)
|
|
117
|
+
.split(',')
|
|
118
|
+
.filter(Boolean)
|
|
119
|
+
))
|
|
120
|
+
);
|
|
64
121
|
}
|
|
65
122
|
get $searchData() {
|
|
66
|
-
|
|
123
|
+
if (!('searchData' in this.$)) {
|
|
124
|
+
this.$.searchData = String(this.searchData || this.name).toLowerCase();
|
|
125
|
+
}
|
|
126
|
+
return this.$.searchData;
|
|
127
|
+
}
|
|
128
|
+
get $dataset() {
|
|
129
|
+
const { $row, $navInfo, $, ...d } = this;
|
|
130
|
+
return d;
|
|
67
131
|
}
|
|
68
132
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Service } from '../services/Service';
|
|
2
|
+
import { analyzeDataByTags, getSuggestions, applyNavInfo, Item } from './Item';
|
|
3
|
+
import { arraySortBy } from '../../lib';
|
|
4
|
+
import { Storage } from '../services/Storage';
|
|
5
|
+
|
|
6
|
+
export class ItemCollectionController extends Service {
|
|
7
|
+
selection: Hash<boolean> = {};
|
|
8
|
+
shownLimit = 40;
|
|
9
|
+
sortBy = 'title';
|
|
10
|
+
kluqPoshuku = '';
|
|
11
|
+
data?: Item[] | null;
|
|
12
|
+
inputKluqPoshuku = '';
|
|
13
|
+
newEntry: any;
|
|
14
|
+
entry: any;
|
|
15
|
+
initials = '';
|
|
16
|
+
tags: Tag[] = [];
|
|
17
|
+
|
|
18
|
+
local = new Storage('local');
|
|
19
|
+
|
|
20
|
+
get searchHistory(): string[] {
|
|
21
|
+
const str = this.local?.get('searchHistory');
|
|
22
|
+
return str ? str.split(',') : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get searchSuggestion() {
|
|
26
|
+
if (this.kluqPoshuku === this.inputKluqPoshuku) {
|
|
27
|
+
if (this.kluqPoshuku) return null;
|
|
28
|
+
return this.searchHistory?.map((s: string) => ({ name: s, id: s }));
|
|
29
|
+
}
|
|
30
|
+
return this.data ? getSuggestions(this.data, this.inputKluqPoshuku) : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
set searchHistory(arr) {
|
|
34
|
+
this.local?.set('searchHistory', (arr || []).join(','));
|
|
35
|
+
}
|
|
36
|
+
addHistoryItem(value: string) {
|
|
37
|
+
if (value) {
|
|
38
|
+
this.searchHistory = [value, ...(this.searchHistory || []).filter((s) => s != value)].slice(0, 20);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
setData(data: Hash[]) {
|
|
42
|
+
this.data = !data ? null : data.map((e) => new Item(e));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get sortedData() {
|
|
46
|
+
const data = this.data;
|
|
47
|
+
if (!data) return null;
|
|
48
|
+
|
|
49
|
+
const searchedData = !this.kluqPoshuku ? data : data.filter((e) => e.$searchData.startsWith(this.kluqPoshuku));
|
|
50
|
+
this.tags = [];
|
|
51
|
+
const actualData = analyzeDataByTags(searchedData, this.selection, this.initials, this.tags, this.labelsFields);
|
|
52
|
+
return applyNavInfo(arraySortBy(actualData, this.sortBy) as unknown as Item[]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getInfo() {
|
|
56
|
+
const data = this.sortedData;
|
|
57
|
+
if (!data) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
tags: this.tags,
|
|
62
|
+
data: this.sortedData?.slice(0, this.shownLimit),
|
|
63
|
+
counts: {
|
|
64
|
+
total: data.length,
|
|
65
|
+
actual: data.length,
|
|
66
|
+
hasMore: this.shownLimit < data.length,
|
|
67
|
+
},
|
|
68
|
+
search: {
|
|
69
|
+
kluq: this.kluqPoshuku,
|
|
70
|
+
value: this.inputKluqPoshuku,
|
|
71
|
+
suggestions: this.searchSuggestion,
|
|
72
|
+
input: this.createEmitter('this.inputKluq'),
|
|
73
|
+
enter: this.createEmitter('this.znajdzPoKluqu'),
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// event handlers:
|
|
79
|
+
|
|
80
|
+
onTag({ id }: Tag, { selection }: this) {
|
|
81
|
+
return {
|
|
82
|
+
selection: !id
|
|
83
|
+
? {}
|
|
84
|
+
: {
|
|
85
|
+
...selection,
|
|
86
|
+
[id]: !selection[id],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
onZnajdzPoKluqu({ value }: StringHash) {
|
|
91
|
+
this.addHistoryItem(value);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
inputKluqPoshuku: value || '',
|
|
95
|
+
kluqPoshuku: value || '',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
onInputKluq({ value }: Delta) {
|
|
99
|
+
return {
|
|
100
|
+
inputKluqPoshuku: value || '',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
onSortBy({ value }: Delta) {
|
|
104
|
+
return {
|
|
105
|
+
sortBy: value || '-title',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
onShowMore({ size = 50 }, { shownLimit }: this) {
|
|
109
|
+
return {
|
|
110
|
+
shownLimit: shownLimit + size,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import { Service } from '../services/Service';
|
|
2
2
|
import { Item } from './Item';
|
|
3
|
-
import { getPredicat } from '
|
|
3
|
+
import { getPredicat } from '../../lib';
|
|
4
4
|
|
|
5
|
+
const CHANGES: Hash<Hash> = {};
|
|
5
6
|
export class ItemController extends Service {
|
|
6
7
|
all?: Hash<Hash>;
|
|
7
|
-
delta?: Hash;
|
|
8
8
|
itemId: string = '';
|
|
9
9
|
meta: any;
|
|
10
10
|
context: any;
|
|
11
11
|
|
|
12
|
+
get initialElement() {
|
|
13
|
+
return Array.isArray(this.all) ? this.all.find((e) => e.id == this.itemId) : this.all?.[this.itemId];
|
|
14
|
+
}
|
|
12
15
|
get initialData() {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
const { $row, ...d } = initial;
|
|
18
|
-
return d;
|
|
16
|
+
const e = this.initialElement;
|
|
17
|
+
return !e?.id ? null : e.$dataset;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
get actualData() {
|
|
@@ -23,10 +22,10 @@ export class ItemController extends Service {
|
|
|
23
22
|
if (!initial) {
|
|
24
23
|
return null;
|
|
25
24
|
}
|
|
26
|
-
const data = { ...this.context, ...initial, ...this.
|
|
25
|
+
const data = { ...this.context, ...initial, ...CHANGES[this.itemId] };
|
|
27
26
|
this.meta?.map((field: { [k: string]: string }) => {
|
|
28
27
|
const { id, visible } = field;
|
|
29
|
-
const isVisible =
|
|
28
|
+
const isVisible = !visible || getPredicat(visible)(data);
|
|
30
29
|
if (!isVisible && data[id]) {
|
|
31
30
|
data[id] = '';
|
|
32
31
|
}
|
|
@@ -38,25 +37,55 @@ export class ItemController extends Service {
|
|
|
38
37
|
return new Item(this.actualData || {});
|
|
39
38
|
}
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
get itemNavInfo() {
|
|
41
|
+
return { prev: null, next: null, ...this.initialElement?.$navInfo };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get itemTitle() {
|
|
45
|
+
return this.initialElement?.title;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get touched() {
|
|
49
|
+
return !!CHANGES[this.itemId];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
upsert(data?: Hash): Hash {
|
|
42
53
|
throw new Error('upsert not defined.' + data);
|
|
43
54
|
}
|
|
44
55
|
|
|
45
|
-
|
|
56
|
+
async submitData(data: Delta) {
|
|
57
|
+
if (!data) return null;
|
|
58
|
+
|
|
59
|
+
const { error, lastUpdatedItemId, ...rest } = await this.upsert(data);
|
|
60
|
+
console.log(error, rest);
|
|
61
|
+
if (error) {
|
|
62
|
+
const msg = (error as Error)?.message;
|
|
63
|
+
this.toast({ id: msg, mode: 'error', message: 'Error: ' + msg });
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
delete CHANGES[this.itemId];
|
|
67
|
+
this.toast({ message: 'Saved successfully.', timeout: 5000, link: '#/task?itemId=' + lastUpdatedItemId });
|
|
68
|
+
return {
|
|
69
|
+
counter: NaN,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
46
72
|
|
|
47
|
-
|
|
73
|
+
// event handlers:
|
|
74
|
+
onChange = (delta: Hash) => {
|
|
75
|
+
CHANGES[this.itemId] = { ...CHANGES[this.itemId], ...delta };
|
|
76
|
+
return {
|
|
77
|
+
counter: NaN,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
48
80
|
|
|
49
81
|
onSave() {
|
|
50
|
-
|
|
51
|
-
if (data) {
|
|
52
|
-
this.upsert(data);
|
|
53
|
-
}
|
|
82
|
+
return this.submitData(this.actualData);
|
|
54
83
|
}
|
|
55
84
|
|
|
56
85
|
onDelete() {
|
|
57
|
-
const id = this.
|
|
58
|
-
if (id)
|
|
59
|
-
|
|
60
|
-
}
|
|
86
|
+
const { id } = this.initialElement?.id;
|
|
87
|
+
if (!id) return null;
|
|
88
|
+
|
|
89
|
+
return this.submitData({ id, status: 'deleted' });
|
|
61
90
|
}
|
|
62
91
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Service } from '../services/Service';
|
|
2
|
+
|
|
3
|
+
export class NewItemController extends Service {
|
|
4
|
+
data: any;
|
|
5
|
+
info: any;
|
|
6
|
+
|
|
7
|
+
upsert(_data: any) {
|
|
8
|
+
throw new Error('Method not implemented.');
|
|
9
|
+
}
|
|
10
|
+
async submitData(data: Delta) {
|
|
11
|
+
if (!data) return null;
|
|
12
|
+
|
|
13
|
+
const { error, lastUpdatedItemId, ...rest } = await this.upsert(data);
|
|
14
|
+
console.log(error, rest);
|
|
15
|
+
if (error) {
|
|
16
|
+
const msg = (error as Error)?.message;
|
|
17
|
+
this.toast({ id: msg, mode: 'error', message: 'Error: ' + msg });
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.toast({ message: 'Added successfully. ', timeout: 5000, link: '#/task?itemId=' + lastUpdatedItemId });
|
|
22
|
+
return {
|
|
23
|
+
info: { open: false, data: {} },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// event handlers:
|
|
27
|
+
|
|
28
|
+
onOpenAddNew() {
|
|
29
|
+
return {
|
|
30
|
+
info: {
|
|
31
|
+
data: this.data,
|
|
32
|
+
open: true,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onCancel() {
|
|
38
|
+
return {
|
|
39
|
+
info: { open: false },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onSubmit(data: Delta) {
|
|
44
|
+
return this.submitData(data);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
<div class="form-input" style="height: auto;min-height:1.5rem;">{value}</div>
|
|
7
7
|
</ui:then>
|
|
8
8
|
<ui:else>
|
|
9
|
-
<div class="has-icon-right" click="{open|
|
|
9
|
+
<div class="has-icon-right" click="{open|fnBindArgs:true}">
|
|
10
10
|
<div class="form-input text-{value|then::gray}" style="height: auto;min-height:1.5rem;">{value|or:@placeholder}</div>
|
|
11
11
|
<i class="form-icon icon icon-edit c-hand"></i>
|
|
12
12
|
</div>
|
|
13
13
|
</ui:else>
|
|
14
14
|
</ui:fragment>
|
|
15
15
|
|
|
16
|
-
<Modal open="{opened}" mode="sm" ui:if="{opened}" close="{open|
|
|
16
|
+
<Modal open="{opened}" mode="sm" ui:if="{opened}" close="{open|fnBindArgs:false}">
|
|
17
17
|
<Modal:header>
|
|
18
18
|
<h6>{caption}</h6>
|
|
19
19
|
</Modal:header>
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
39
|
<Modal:footer>
|
|
40
|
-
<Btn class="float-right" mode="primary" title="OK" action="{open|
|
|
40
|
+
<Btn class="float-right" mode="primary" title="OK" action="{open|fnBindArgs:false}"/>
|
|
41
41
|
</Modal:footer>
|
|
42
42
|
</Modal>
|
|
43
43
|
</div>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<component id="SuggestionInput">
|
|
2
2
|
<div class="form-autocomplete">
|
|
3
3
|
<div class="has-icon-right">
|
|
4
|
-
<input class="form-input" type="text" disabled="{disabled}" placeholder="{placeholder}" focus="{open|
|
|
4
|
+
<input class="form-input" type="text" disabled="{disabled}" placeholder="{placeholder}" focus="{open|fnBindArgs:true}" blur2="{open|fnBindArgs:false|fnTick:200}" keypress="{change}" value="{value}" />
|
|
5
5
|
<i class="form-icon icon icon-{value|then:cross:arrow-down} c-hand" style="margin-right:{options.length|then:1.5:0.25}rem;" click="{reset}" ui:if="disabled|not|and:@value"></i>
|
|
6
6
|
<ui:fragment ui:if="options|and:@options.length">
|
|
7
|
-
<i class="form-icon icon icon-arrow-up c-hand" click="{open|
|
|
8
|
-
<i class="form-icon icon icon-arrow-down c-hand" click="{open|
|
|
7
|
+
<i class="form-icon icon icon-arrow-up c-hand" click="{open|fnBindArgs:false}" ui:if="opened"></i>
|
|
8
|
+
<i class="form-icon icon icon-arrow-down c-hand" click="{open|fnBindArgs:true}" ui:if="opened|not"></i>
|
|
9
9
|
</ui:fragment>
|
|
10
10
|
</div>
|
|
11
11
|
<ul class="menu" style="position2: relative; min-width: 180px;max-height:7rem;overflow-y:auto;" ui:if="{options|and:@options.length|and:@opened}">
|