arrmatura 5.2.0 → 6.3.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.
Files changed (116) hide show
  1. package/README.md +29 -38
  2. package/docs/glossary.md +19 -0
  3. package/docs/hello.md +46 -0
  4. package/docs/manual.md +294 -0
  5. package/index.ts +21 -0
  6. package/package.json +16 -44
  7. package/src/core/Component.ts +67 -0
  8. package/src/core/Entitron.ts +406 -0
  9. package/src/core/ManifestNode.ts +139 -0
  10. package/src/core/resolveExpression.ts +20 -0
  11. package/src/registry/composition.ts +66 -0
  12. package/src/registry/conditionals.ts +75 -0
  13. package/src/registry/elementary.ts +36 -0
  14. package/src/registry/fragment.ts +21 -0
  15. package/src/registry/index.ts +69 -0
  16. package/src/registry/iterations.ts +122 -0
  17. package/src/registry/root.ts +46 -0
  18. package/src/registry/routing.ts +31 -0
  19. package/src/registry/selection.ts +57 -0
  20. package/src/registry/slot.ts +36 -0
  21. package/src/utils/compileExpression.ts +108 -0
  22. package/src/utils/stringify.ts +33 -0
  23. package/types.ts +73 -0
  24. package/LICENSE +0 -21
  25. package/commons/auth/AuthService.ts +0 -111
  26. package/commons/auth/UserService.ts +0 -57
  27. package/commons/auth/index.ts +0 -5
  28. package/commons/auth/user.xml +0 -64
  29. package/commons/components/buttons.xml +0 -25
  30. package/commons/components/index.ts +0 -13
  31. package/commons/components/inputs.xml +0 -33
  32. package/commons/components/layouts.xml +0 -77
  33. package/commons/components/media.xml +0 -21
  34. package/commons/components/modal.xml +0 -37
  35. package/commons/components/navigation.xml +0 -73
  36. package/commons/components/popups.xml +0 -12
  37. package/commons/components/searchbar.xml +0 -54
  38. package/commons/components/table.xml +0 -140
  39. package/commons/components/toasts.xml +0 -18
  40. package/commons/components/viewport.xml +0 -48
  41. package/commons/forms/FieldItem.xml +0 -20
  42. package/commons/forms/FormController.ts +0 -87
  43. package/commons/forms/Item.ts +0 -132
  44. package/commons/forms/ItemCollectionController.ts +0 -113
  45. package/commons/forms/ItemController.ts +0 -91
  46. package/commons/forms/MultiEnumField.xml +0 -44
  47. package/commons/forms/MultivalueController.ts +0 -49
  48. package/commons/forms/NewItemController.ts +0 -46
  49. package/commons/forms/SmartareaField.xml +0 -51
  50. package/commons/forms/SuggestionController.ts +0 -80
  51. package/commons/forms/SuggestionField.xml +0 -28
  52. package/commons/forms/fields.xml +0 -151
  53. package/commons/forms/form.xml +0 -37
  54. package/commons/forms/index.ts +0 -29
  55. package/commons/index.ts +0 -6
  56. package/commons/services/ApiClientService.ts +0 -32
  57. package/commons/services/ErrorHandlingService.ts +0 -11
  58. package/commons/services/FirebaseService.ts +0 -185
  59. package/commons/services/GSheetsService.ts +0 -120
  60. package/commons/services/HeartbeatService.ts +0 -35
  61. package/commons/services/Invoke.ts +0 -29
  62. package/commons/services/JsonpService.ts +0 -18
  63. package/commons/services/Loader.ts +0 -17
  64. package/commons/services/MetadataService.ts +0 -50
  65. package/commons/services/NavigationService.ts +0 -74
  66. package/commons/services/Service.ts +0 -62
  67. package/commons/services/ServiceWorker.ts +0 -111
  68. package/commons/services/Storage.ts +0 -55
  69. package/commons/services/ToastService.ts +0 -61
  70. package/commons/services/index.ts +0 -13
  71. package/core/Arrmatura.ts +0 -79
  72. package/core/CNode.ts +0 -111
  73. package/core/Component.ts +0 -424
  74. package/core/cnodes/composition.ts +0 -100
  75. package/core/cnodes/conditionals.ts +0 -52
  76. package/core/cnodes/elementary.ts +0 -44
  77. package/core/cnodes/iterations.ts +0 -92
  78. package/core/cnodes/routing.ts +0 -25
  79. package/core/compileNode.ts +0 -39
  80. package/core/expression.ts +0 -73
  81. package/core/index.ts +0 -1
  82. package/core/register.ts +0 -43
  83. package/core/utils.ts +0 -23
  84. package/core-web/WebPlatform.ts +0 -25
  85. package/core-web/attributes.ts +0 -218
  86. package/core-web/elements.ts +0 -81
  87. package/core-web/index.ts +0 -28
  88. package/core-web/reflow.ts +0 -30
  89. package/core-web/support.ts +0 -27
  90. package/core-web/type.ts +0 -18
  91. package/docs/index.js +0 -485
  92. package/lib/array/arrayGroupBy.ts +0 -19
  93. package/lib/array/arraySortBy.ts +0 -49
  94. package/lib/array/arrayToObject.ts +0 -18
  95. package/lib/array/arrayish.ts +0 -7
  96. package/lib/array/index.ts +0 -4
  97. package/lib/crypto/index.ts +0 -25
  98. package/lib/date/index.ts +0 -127
  99. package/lib/func/curry.ts +0 -11
  100. package/lib/func/fx.ts +0 -56
  101. package/lib/func/identity.ts +0 -6
  102. package/lib/func/nextId.ts +0 -9
  103. package/lib/func/noop.ts +0 -6
  104. package/lib/geo/index.js +0 -22
  105. package/lib/index.ts +0 -17
  106. package/lib/object/index.ts +0 -69
  107. package/lib/parsing/codus.ts +0 -25
  108. package/lib/parsing/compiler.ts +0 -26
  109. package/lib/parsing/scalarParse.ts +0 -25
  110. package/lib/parsing/urls.ts +0 -91
  111. package/lib/parsing/xml.ts +0 -117
  112. package/lib/resources/l10n.ts +0 -40
  113. package/lib/resources/resources.ru.ts +0 -20
  114. package/lib/resources/resources.ts +0 -20
  115. package/lib/string/index.ts +0 -58
  116. package/lib/support/createListeners.ts +0 -12
@@ -1,140 +0,0 @@
1
- <component id="LoadingIndicator">
2
- <div class="loading {large|then:loading-lg} {class}" />
3
- </component>
4
-
5
- <component id="EmptyData">
6
- <div class="empty w100 m-0 {class}">
7
- <div class="empty-icon">
8
- <Icon type="{icon|or:people}" />
9
- </div>
10
- <p class="empty-title h5">{title}</p>
11
- <p ui:if="{subtitle}" class="empty-subtitle">{subtitle}</p>
12
- <div class="empty-action">
13
- <ui:slot />
14
- </div>
15
- </div>
16
- </component>
17
-
18
- <component id="AsyncLoader">
19
- <ui:fragment ui:if="isLoading">
20
- <ui:then>
21
- <EmptyData class="h100" title="{title}" subtitle="{error|then::@subtitle}" icon="{icon|or:download}">
22
- <ui:fragment ui:if="error|not">
23
- <ui:then>
24
- <LoadingIndicator/>
25
- </ui:then>
26
- <ui:else>
27
- <Btn mode="error" action="{onError}" title="Error: {error}"/>
28
- </ui:else>
29
- </ui:fragment>
30
- </EmptyData>
31
- </ui:then>
32
- <ui:else>
33
- <ui:slot />
34
- </ui:else>
35
- </ui:fragment>
36
- </component>
37
-
38
- <component id="LoadableItems">
39
- <LoadingIndicator class="empty" ui:if="data|not">
40
- <ui:else>
41
- <h5 class="error text-error" ui:if="data.error">{data.error.message|or:@data.error}</h5>
42
- <EmptyData title="{emptyCaption|or:Empty}" ui:if="data.length|not">
43
- <ui:else>
44
- <div style="{style}" ui:for="item of data">
45
- <ui:tag tag="{itemType}" ui:props="{item}" item="{item}" context="{context}" action="{action}" />
46
- </div>
47
- </ui:else>
48
- </EmptyData>
49
- </ui:else>
50
- </LoadingIndicator>
51
- </component>
52
-
53
- <component id="LoadableEntity">
54
- <LoadingIndicator class="empty" ui:if="data|not">
55
- <ui:else>
56
- <h5 class="error text-error" ui:if="data.error">
57
- <span>{data.error.message|or:@data.error}</span>
58
- <ui:else>
59
- <EmptyData title="{emptyCaption|or:Empty}" ui:if="data.id|not">
60
- <ui:else>
61
- <ui:tag tag="{type}" data="{data}" context="{context}" action="{action}" />
62
- </ui:else>
63
- </EmptyData>
64
- </ui:else>
65
- </h5>
66
- </ui:else>
67
- </LoadingIndicator>
68
- </component>
69
-
70
- <component id="Cell">
71
- <span class="" style="padding-left: 0px">{value}</span>
72
- </component>
73
-
74
- <component id="EnumCell">
75
- <span class="" style="padding-left: 0px">:map.{typeSpec}.{value}</span>
76
- </component>
77
-
78
- <component id="DateCell">
79
- <span class="" style="padding-left: 0px">{value|date}</span>
80
- </component>
81
-
82
- <component id="Table">
83
- <table style="position: relative">
84
- <thead class="table-thead">
85
- <tr>
86
- <th class="col-index" style="position: sticky; top: -1"></th>
87
- <th class="col-index" style="position: sticky; top: -1" ui:for="item of columns">
88
- <span>{item.name}</span>
89
- <div class="table-column-sorter" ui:if="item.sortable">
90
- <span class="table-column-sorter-up off" title="↑" data-id="-{item.id}" click="{doSort}">
91
- <i class="anticon anticon-caret-up"></i
92
- > </span>
93
- <span class="table-column-sorter-down off" title="↓" data-id="{item.id}" click="{doSort}">
94
- <i class="anticon anticon-caret-down"></i
95
- > </span>
96
- </div>
97
- </th>
98
- </tr>
99
- </thead>
100
- <TableRow ui:for="item of data" data="{item}" selected="{item.selected}" columns="{columns}" kind="{kind}" />
101
- </table>
102
- </component>
103
-
104
- <component id="TableRow">
105
- <tbody class="table-tbody">
106
- <tr class="table-row table-row-level-0" style="position: sticky">
107
- <td class="col-index" data-id="{data.id}" click="{doItem}">
108
- <div ui:if="{expandable}" role="button" class="table-row-expand-icon table-row-expanded" aria-label="Collapse row" data-selected="false" click="->">
109
- <ui:else>
110
- <div role="button" class="table-row-expand-icon table-row-collapsed" aria-label="Expand row" data-selected="true" click="->"></div>
111
- </ui:else>
112
- </div>
113
- </td>
114
- <td class="col-index" ui:for="col of columns">
115
- <ui:tag tag="{col.type|capitalize}Cell" ui:props="{col}" value="{col.id|propObjectValueByKey}" data="{data}" />
116
- </td>
117
- </tr>
118
- <tr ui:if="{expanded}" class="table-expanded-row table-expanded-row-level-1">
119
- <td class=""></td>
120
- <td class="" colspan="100">
121
- <ui:tag tag="{kind|capitalize}RowInsider" data="{data}" />
122
- </td>
123
- </tr>
124
- </tbody>
125
- </component>
126
-
127
- <component id="Chips">
128
- <div class="chips mx-0 py-2 px-2 {class}">
129
- <div class="ui mini labels">
130
- <span class="chip {tag.selected|then:bg-success:bg-secondary} {tag.nonSelectable|then::c-hand} {tag.count|then:badge}" data-badge="{tag.count}" style="padding:1.2rem" data-id="{tag.id}" click="{tag.nonSelectable|then:undefined:@click}" ui:for="tag of data">
131
- <span>
132
- <b>{tag.name}</b>
133
- </span>
134
- </span>
135
- <span ui:if="data|find:true:selected" class="label py-1 px-2 s-circle c-hand" click="{click|assignFirstArgKeyValue:id:}">
136
- <Icon type="cross"/>
137
- </span>
138
- </div>
139
- </div>
140
- </component>
@@ -1,18 +0,0 @@
1
- <component id="ToastList">
2
- <div style="position:absolute;top:2.5rem;right:1rem;left:1rem;z-index:500;{style}">
3
- <div style="max-width: 600px;margin: auto;">
4
- <Toast ui:props={toast} ui:for="toast of <-toasters.items"/>
5
- </div>
6
- </div>
7
- </component>
8
-
9
- <component id="Toast">
10
- <div class="toast mt-1 toast-{mode|or:@mode|or:primary}">
11
- <button class="btn btn-clear float-right" click="{close}"></button>
12
- <p>
13
- <b ui:if="counter" class="mr-1">[x{counter}]</b>
14
- <span> {message}</span>
15
- <a class="mx-1" href="{link}" ui:if="link">link</a>
16
- </p>
17
- </div>
18
- </component>
@@ -1,48 +0,0 @@
1
- <component id="ViewPort">
2
- <ui:fragment ui:if="slot(aside)">
3
- <Sidebar caption="{caption}" size="{sidebarWidth}">
4
- <Sidebar:aside>
5
- <ui:slot id="aside" />
6
- </Sidebar:aside>
7
- <main>
8
- <ui:slot />
9
- </main>
10
- </Sidebar>
11
- <ui:else>
12
- <main>
13
- <ui:slot />
14
- </main>
15
- </ui:else>
16
- </ui:fragment>
17
- </component>
18
-
19
- <component id="Sidebar">
20
- <div class="off-canvas off-canvas-sidebar-show">
21
- <a class="off-canvas-toggle btn btn-primary btn-action show-lg" href="#sidebar">
22
- <Icon type="menu" />
23
- </a>
24
- <div id="sidebar" class="off-canvas-sidebar">
25
- <Aside caption="{caption}">
26
- <ui:slot id="aside" />
27
- </Aside>
28
- </div>
29
- <a class="off-canvas-overlay" href="#"></a>
30
- <div class="off-canvas-content">
31
- <ui:slot />
32
- </div>
33
- </div>
34
- </component>
35
-
36
- <component id="Aside">
37
- <aside style="display:flex; flex-direction: column; height: 100vh; width:{size|or:300}px">
38
- <div class="text-center" ui:if="{caption}">
39
- <a href="#/main">
40
- <h5 class="p-2">{caption}</h5>
41
- </a>
42
- </div>
43
- <div class="m-2" style="flex: 1; overflow-y: auto">
44
- <ui:slot />
45
- </div>
46
- </aside>
47
- </component>
48
-
@@ -1,20 +0,0 @@
1
- <component id="FieldItem">
2
- <div class="form-group {error|then:has-error} {class}">
3
- <div class="col-4 col-sm-12">
4
- <label class="form-label d-flex" for="input-example-1" ui:if="{caption}">
5
- <div class="text-ellipsis" title="{caption}">{caption}</div>
6
- <div ui:if="{required}" class="text-error text-tiny ml-1">✱</div>
7
- <div class="border-1 text-secondary s-circle centroid mx-1 px-2" style="height:1.3rem;width:1.3rem;" data-value="{showHint|not}" click="->showHint|dot:value" ui:if="{hint}">
8
- <span>?</span>
9
- </div>
10
- </label>
11
- </div>
12
- <div class="col-8 col-sm-12">
13
- <ui:slot />
14
- </div>
15
- <div class="col-12">
16
- <p class="form-input-hint mb-1" ui:if="{showHint}">{hint}</p>
17
- <p class="form-input-hint mb-1" ui:if="{error}">{error}</p>
18
- </div>
19
- </div>
20
- </component>
@@ -1,87 +0,0 @@
1
- import { arrayGroupBy } from '../../lib';
2
- import { strFormat, getPredicat } from '../../lib';
3
- import { Service } from '../services/Service';
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
-
12
- export class FormController extends Service {
13
- data: { [k: string]: unknown } = {};
14
- meta: any;
15
- error: any;
16
- change: any;
17
- tabs?: StringHash;
18
- groups?: StringHash;
19
-
20
- setData(d: Data) {
21
- const data = { ...d };
22
- this.data = data;
23
- }
24
-
25
- get fieldsGroups(): Obj[] {
26
- return getFieldsGroups(this.fieldProps, this.groups);
27
- }
28
-
29
- get tabsInfo() {
30
- const tabs = Object.values(arrayGroupBy(this.fieldProps, ({ tab = 'tab' }) => tab)).map(({ id, items }) => ({
31
- id,
32
- name: this.tabs?.[id] || id,
33
- items: getFieldsGroups(items, this.groups),
34
- }));
35
- return { enabled: tabs.length > 1, initialTab: tabs?.[0]?.id, tabs };
36
- }
37
-
38
- resolveEnum(typeSpec: string = '', data: Hash) {
39
- if (typeSpec[0] === '[') {
40
- return typeSpec
41
- .slice(1, typeSpec.length - 1)
42
- .split(',')
43
- .map((s) => s.trim().split('='))
44
- .map(([id, name = id]) => ({ id, name }));
45
- }
46
-
47
- if (!typeSpec.includes('.')) return this.res('enums.' + typeSpec) || [];
48
-
49
- const [key, subid, subKey] = strFormat(typeSpec, data).split('.');
50
- const enumItems = this.res('enums.' + key) || [];
51
- if (subKey) {
52
- return enumItems.find(({ id }) => id === subid)?.[subKey] || [];
53
- }
54
- return enumItems;
55
- }
56
-
57
- get fieldProps() {
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
- );
86
- }
87
- }
@@ -1,132 +0,0 @@
1
- import { arraySortBy } from '../../lib';
2
-
3
- export const analyzeDataByTags = (
4
- data: Item[],
5
- selection: Hash<boolean>,
6
- initials = '',
7
- tags: Tag[] = [],
8
- labelsFields: string
9
- ) => {
10
- const sel = Object.entries(selection)
11
- .filter(([_, val]) => val)
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
- }
22
-
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 });
46
- if (!tag.selected) {
47
- tag.nonSelectable = false;
48
- tag.count++;
49
- }
50
- });
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;
56
- } else {
57
- tags.push(...initials.split(',').map((id) => ({ id, name: id, count: 0, selected: false })));
58
- data.forEach((e: Item) => {
59
- tags.forEach((tag) => {
60
- if (e.getLabels().has(tag.id)) {
61
- tag.count++;
62
- }
63
- });
64
- });
65
- return data;
66
- }
67
- };
68
-
69
- export const getSuggestions = (data: Item[], kluq: string) => {
70
- if (!kluq) return null;
71
-
72
- const words: Hash = {};
73
- data.forEach((e) =>
74
- e.$searchData.replace(/[a-z0-9а-яёўі]{3,}/g, (w) => {
75
- words[w] = 1;
76
- return '';
77
- })
78
- );
79
- const lokey = kluq.toLowerCase();
80
- return arraySortBy(
81
- Object.keys(words)
82
- .filter((e) => e.startsWith(lokey))
83
- .map((s) => ({ name: s, id: s })),
84
- 'name'
85
- );
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
- };
100
-
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);
110
- }
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
- );
121
- }
122
- get $searchData() {
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;
131
- }
132
- }
@@ -1,113 +0,0 @@
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,91 +0,0 @@
1
- import { Service } from '../services/Service';
2
- import { Item } from './Item';
3
- import { getPredicat } from '../../lib';
4
-
5
- const CHANGES: Hash<Hash> = {};
6
- export class ItemController extends Service {
7
- all?: Hash<Hash>;
8
- itemId: string = '';
9
- meta: any;
10
- context: any;
11
-
12
- get initialElement() {
13
- return Array.isArray(this.all) ? this.all.find((e) => e.id == this.itemId) : this.all?.[this.itemId];
14
- }
15
- get initialData() {
16
- const e = this.initialElement;
17
- return !e?.id ? null : e.$dataset;
18
- }
19
-
20
- get actualData() {
21
- const initial = this.initialData;
22
- if (!initial) {
23
- return null;
24
- }
25
- const data = { ...this.context, ...initial, ...CHANGES[this.itemId] };
26
- this.meta?.map((field: { [k: string]: string }) => {
27
- const { id, visible } = field;
28
- const isVisible = !visible || getPredicat(visible)(data);
29
- if (!isVisible && data[id]) {
30
- data[id] = '';
31
- }
32
- });
33
- return data;
34
- }
35
-
36
- get item() {
37
- return new Item(this.actualData || {});
38
- }
39
-
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 {
53
- throw new Error('upsert not defined.' + data);
54
- }
55
-
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
- }
72
-
73
- // event handlers:
74
- onChange = (delta: Hash) => {
75
- CHANGES[this.itemId] = { ...CHANGES[this.itemId], ...delta };
76
- return {
77
- counter: NaN,
78
- };
79
- };
80
-
81
- onSave() {
82
- return this.submitData(this.actualData);
83
- }
84
-
85
- onDelete() {
86
- const { id } = this.initialElement?.id;
87
- if (!id) return null;
88
-
89
- return this.submitData({ id, status: 'deleted' });
90
- }
91
- }