@itfin/components 1.4.36 → 1.5.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/package.json +17 -20
- package/src/ITFSettings.js +6 -0
- package/src/assets/scss/_css_variables.scss +2 -7
- package/src/assets/scss/_dark-theme.scss +2 -12
- package/src/assets/scss/_variables.scss +34 -9
- package/src/assets/scss/components/_button.scss +147 -10
- package/src/assets/scss/components/_checkbox.scss +9 -0
- package/src/assets/scss/components/_datepicker.scss +3 -3
- package/src/assets/scss/components/_pagination.scss +4 -1
- package/src/assets/scss/components/_popover.scss +22 -0
- package/src/assets/scss/components/_segmeneted-control.scss +19 -8
- package/src/assets/scss/components/_select.scss +6 -8
- package/src/assets/scss/components/_text-field.scss +27 -11
- package/src/assets/scss/components/select/_dropdown-menu.scss +1 -0
- package/src/assets/scss/components/select/_dropdown-toggle.scss +0 -1
- package/src/assets/scss/directives/tooltip.scss +10 -5
- package/src/assets/scss/main.scss +48 -0
- package/src/components/alert/AlertBanner.vue +75 -0
- package/src/components/app/App.vue +6 -3
- package/src/components/button/Button.vue +3 -1
- package/src/components/button/NativeButton.js +4 -0
- package/src/components/button/index.stories.js +2 -2
- package/src/components/checkbox/Checkbox.vue +2 -1
- package/src/components/checkbox/RadioBox.vue +13 -7
- package/src/components/copyToClipboard/CopyToClipboard.vue +4 -1
- package/src/components/customize/PropertiesList.vue +0 -2
- package/src/components/customize/PropertiesPopupMenu.vue +1 -1
- package/src/components/customize/PropertyItem.vue +6 -24
- package/src/components/datepicker/DatePicker.vue +3 -1
- package/src/components/datepicker/DatePickerInline.vue +2 -2
- package/src/components/datepicker/DateRangePickerInline.vue +6 -1
- package/src/components/dropdown/Dropdown.vue +1 -1
- package/src/components/dropdown/DropdownMenu.vue +1 -1
- package/src/components/editable/EditButton.vue +1 -1
- package/src/components/filter/FilterBadge.vue +20 -1
- package/src/components/filter/FilterFacetsList.vue +67 -13
- package/src/components/filter/FilterPanel.vue +8 -4
- package/src/components/filter/NewFilter.vue +305 -0
- package/src/components/form/Label.vue +5 -5
- package/src/components/icon/components/nomi-ai-alt.vue +5 -0
- package/src/components/icon/components/nomi-arrow-right.vue +4 -0
- package/src/components/icon/components/nomi-bell.vue +5 -0
- package/src/components/icon/components/nomi-calendar-payment.vue +10 -0
- package/src/components/icon/components/nomi-card-plus.vue +1 -0
- package/src/components/icon/components/nomi-cash-provider.vue +9 -0
- package/src/components/icon/components/nomi-cash-repeat.vue +6 -0
- package/src/components/icon/components/nomi-category-edit.vue +5 -0
- package/src/components/icon/components/nomi-chavron-up.vue +4 -0
- package/src/components/icon/components/nomi-chavron_down.vue +4 -0
- package/src/components/icon/components/nomi-chavron_up.vue +4 -0
- package/src/components/icon/components/nomi-chevron-up.vue +4 -0
- package/src/components/icon/components/nomi-exit-right.vue +4 -0
- package/src/components/icon/components/nomi-help.vue +2 -3
- package/src/components/icon/components/nomi-history.vue +7 -0
- package/src/components/icon/components/nomi-lock.vue +1 -1
- package/src/components/icon/components/nomi-pen-alt.vue +4 -0
- package/src/components/icon/components/nomi-project.vue +2 -2
- package/src/components/icon/components/nomi-refresh-off.vue +4 -0
- package/src/components/icon/components/nomi-refresh.vue +4 -0
- package/src/components/icon/components/nomi-scissors.vue +1 -1
- package/src/components/icon/components/nomi-start.vue +28 -0
- package/src/components/icon/components/nomi-table-view.vue +1 -4
- package/src/components/icon/components/nomi-transactions-delete.vue +5 -0
- package/src/components/icon/components/nomi-type-array.vue +6 -0
- package/src/components/icon/components/nomi-type-boolean.vue +5 -0
- package/src/components/icon/components/nomi-type-date.vue +4 -0
- package/src/components/icon/components/nomi-type-null.vue +4 -0
- package/src/components/icon/components/nomi-type-number.vue +4 -0
- package/src/components/icon/components/nomi-type-object.vue +4 -0
- package/src/components/icon/components/nomi-type-string.vue +4 -0
- package/src/components/icon/components/nomi-unarchive.vue +17 -0
- package/src/components/icon/components/nomi-unlink.vue +10 -0
- package/src/components/icon/components/nomi-user.vue +3 -3
- package/src/components/icon/components/nomi-warning-triangle.vue +6 -0
- package/src/components/icon/components/nomi-warning_triangle_filled.vue +6 -0
- package/src/components/icon/convert-icons.js +3 -0
- package/src/components/icon/icons.js +390 -312
- package/src/components/icon/new-icons/ai-alt.svg +4 -0
- package/src/components/icon/new-icons/arrow-right-alt.svg +3 -0
- package/src/components/icon/new-icons/arrow-right.svg +3 -0
- package/src/components/icon/new-icons/arrow_left.svg +3 -0
- package/src/components/icon/new-icons/automation.svg +4 -0
- package/src/components/icon/new-icons/balance.svg +3 -0
- package/src/components/icon/new-icons/balance_turnover.svg +4 -0
- package/src/components/icon/new-icons/bar-horizontal.svg +6 -0
- package/src/components/icon/new-icons/bell.svg +4 -0
- package/src/components/icon/new-icons/calendar-payment.svg +9 -0
- package/src/components/icon/new-icons/card-plus.svg +1 -0
- package/src/components/icon/new-icons/cash-provider.svg +8 -0
- package/src/components/icon/new-icons/cash-repeat.svg +5 -0
- package/src/components/icon/new-icons/cash.svg +3 -0
- package/src/components/icon/new-icons/cashflow.svg +3 -0
- package/src/components/icon/new-icons/category-edit.svg +4 -0
- package/src/components/icon/new-icons/category.svg +4 -0
- package/src/components/icon/new-icons/category_alt.svg +3 -0
- package/src/components/icon/new-icons/chart-bars.svg +5 -0
- package/src/components/icon/new-icons/chart-donut.svg +3 -0
- package/src/components/icon/new-icons/chart-funnel.svg +5 -0
- package/src/components/icon/new-icons/chart-kpi.svg +7 -0
- package/src/components/icon/new-icons/chart-line.svg +4 -0
- package/src/components/icon/new-icons/chart-lines.svg +5 -0
- package/src/components/icon/new-icons/check-alt.svg +3 -0
- package/src/components/icon/new-icons/check.svg +3 -0
- package/src/components/icon/new-icons/chevron-down.svg +3 -0
- package/src/components/icon/new-icons/chevron-left.svg +3 -0
- package/src/components/icon/new-icons/chevron-right.svg +3 -0
- package/src/components/icon/new-icons/chevron-up.svg +3 -0
- package/src/components/icon/new-icons/collapse.svg +6 -0
- package/src/components/icon/new-icons/control-panel.svg +7 -0
- package/src/components/icon/new-icons/credit.svg +3 -0
- package/src/components/icon/new-icons/currencies.svg +3 -0
- package/src/components/icon/new-icons/debt.svg +3 -0
- package/src/components/icon/new-icons/demo.svg +6 -0
- package/src/components/icon/new-icons/dev.svg +3 -0
- package/src/components/icon/new-icons/dots.svg +5 -0
- package/src/components/icon/new-icons/duplicate.svg +4 -0
- package/src/components/icon/new-icons/exit-right.svg +3 -0
- package/src/components/icon/new-icons/export.svg +3 -0
- package/src/components/icon/new-icons/file.svg +3 -0
- package/src/components/icon/new-icons/folder.svg +3 -0
- package/src/components/icon/new-icons/goods-turnover.svg +3 -0
- package/src/components/icon/new-icons/goods.svg +4 -0
- package/src/components/icon/new-icons/help-alt.svg +3 -0
- package/src/components/icon/new-icons/help.svg +2 -3
- package/src/components/icon/new-icons/history.svg +6 -0
- package/src/components/icon/new-icons/integration.svg +3 -0
- package/src/components/icon/new-icons/link.svg +5 -0
- package/src/components/icon/new-icons/lock.svg +1 -1
- package/src/components/icon/new-icons/menu.svg +5 -0
- package/src/components/icon/new-icons/minus.svg +3 -0
- package/src/components/icon/new-icons/payment_calendar.svg +3 -0
- package/src/components/icon/new-icons/pc.svg +3 -0
- package/src/components/icon/new-icons/pen-alt.svg +3 -0
- package/src/components/icon/new-icons/planFact.svg +4 -0
- package/src/components/icon/new-icons/pnl.svg +7 -0
- package/src/components/icon/new-icons/project.svg +2 -2
- package/src/components/icon/new-icons/project_alt.svg +3 -0
- package/src/components/icon/new-icons/project_alt2.svg +3 -0
- package/src/components/icon/new-icons/promo.svg +3 -0
- package/src/components/icon/new-icons/refresh-off.svg +3 -0
- package/src/components/icon/new-icons/refresh.svg +3 -0
- package/src/components/icon/new-icons/scissors.svg +1 -1
- package/src/components/icon/new-icons/segment.svg +3 -0
- package/src/components/icon/new-icons/start.svg +27 -0
- package/src/components/icon/new-icons/strongbox.svg +3 -0
- package/src/components/icon/new-icons/subscription.svg +3 -0
- package/src/components/icon/new-icons/table-view.svg +1 -4
- package/src/components/icon/new-icons/time.svg +3 -0
- package/src/components/icon/new-icons/transactions_alt.svg +6 -0
- package/src/components/icon/new-icons/transactions_delete.svg +4 -0
- package/src/components/icon/new-icons/type-array.svg +5 -0
- package/src/components/icon/new-icons/type-boolean.svg +4 -0
- package/src/components/icon/new-icons/type-date.svg +3 -0
- package/src/components/icon/new-icons/type-null.svg +3 -0
- package/src/components/icon/new-icons/type-number.svg +3 -0
- package/src/components/icon/new-icons/type-object.svg +3 -0
- package/src/components/icon/new-icons/type-string.svg +3 -0
- package/src/components/icon/new-icons/types.svg +6 -0
- package/src/components/icon/new-icons/unarchive.svg +16 -0
- package/src/components/icon/new-icons/unlink.svg +9 -0
- package/src/components/icon/new-icons/user.svg +3 -3
- package/src/components/icon/new-icons/user_plus.svg +10 -0
- package/src/components/icon/new-icons/warehouse.svg +3 -0
- package/src/components/icon/new-icons/warning_triangle.svg +5 -0
- package/src/components/icon/new-icons/warning_triangle_filled.svg +5 -0
- package/src/components/kanban/BoardCard.vue +1 -1
- package/src/components/kanban/BoardCardTimer.vue +1 -1
- package/src/components/modal/DeleteConfirmModal.vue +10 -6
- package/src/components/modal/ItemEditor.vue +1 -1
- package/src/components/modal/Modal.vue +1 -1
- package/src/components/overlay/SensitiveOverlay.vue +2 -4
- package/src/components/panels/Panel.vue +110 -23
- package/src/components/panels/PanelItemEdit.vue +8 -6
- package/src/components/panels/PanelList.vue +163 -40
- package/src/components/panels/helpers.ts +29 -11
- package/src/components/popover/Popover.vue +105 -22
- package/src/components/segmented-control/SegmentedControl.vue +9 -3
- package/src/components/sortable/draggable.js +1 -1
- package/src/components/table/Table2.vue +68 -67
- package/src/components/table/TableBody.vue +17 -22
- package/src/components/table/TableGroup.vue +40 -24
- package/src/components/table/TableHeader.vue +86 -81
- package/src/components/table/TableRowToggle.vue +1 -9
- package/src/components/table/TableRows.vue +49 -55
- package/src/components/table/mobile.js +4 -0
- package/src/components/table/table2.scss +34 -15
- package/src/components/text-field/MoneyField.vue +10 -4
- package/src/components/text-field/TextField.vue +17 -8
- package/src/components/tree/TreeEditor.vue +3 -2
- package/src/components/view/View.vue +73 -208
- package/src/directives/appendToBody.js +1 -0
- package/src/helpers/validators.js +9 -35
- package/src/helpers/validators.spec.js +11 -48
- package/src/locales/en.js +4 -6
- package/src/locales/pl.js +158 -0
- package/src/locales/uk.js +6 -7
- package/src/components/icon/components/nomi-calendar-view.vue +0 -4
- package/src/components/icon/components/nomi-kanban-view.vue +0 -6
- package/src/components/icon/components/nomi-list-view.vue +0 -7
- package/src/components/icon/components/nomi-table-config.vue +0 -9
- package/src/components/icon/new-icons/calendar-view.svg +0 -3
- package/src/components/icon/new-icons/kanban-view.svg +0 -5
- package/src/components/icon/new-icons/list-view.svg +0 -6
- package/src/components/icon/new-icons/table-config.svg +0 -8
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<template v-for="(panel, n) of panelsStack">
|
|
11
11
|
<panel
|
|
12
12
|
:key="n"
|
|
13
|
+
:ref="`panel-${panel.id}`"
|
|
13
14
|
:index="n"
|
|
14
15
|
:panel="panel"
|
|
15
16
|
:title="panel.title"
|
|
@@ -17,14 +18,22 @@
|
|
|
17
18
|
:icon="panel.icon"
|
|
18
19
|
:payload="panel.payload"
|
|
19
20
|
:expandable="panelsStack.length > 1"
|
|
21
|
+
:isFullSize="isFullSize"
|
|
20
22
|
:collapsed="panel.isCollapsed"
|
|
21
23
|
:closeable="panel.isCloseable"
|
|
22
24
|
:animate="panel.isAnimate"
|
|
23
25
|
@open="openPanel($event[0], $event[1], n + 1)"
|
|
24
26
|
@expand="expandPanel(panel)"
|
|
25
27
|
@fullsize="fullsizePanel(panel)"
|
|
28
|
+
@collapse="collapsePanel(panel)"
|
|
26
29
|
@close="closePanel(panel)"
|
|
30
|
+
@open-menu="$emit('open-menu', panel.type, panel.payload)"
|
|
31
|
+
@onboarding-progress="$emit('onboarding-progress', $event)"
|
|
32
|
+
@onboarding-step-viewed="$emit('onboarding-step-viewed', $event)"
|
|
27
33
|
>
|
|
34
|
+
<template #before-header>
|
|
35
|
+
<slot name="before-header" :panel="panel" :index="n" :payload="panel.payload"></slot>
|
|
36
|
+
</template>
|
|
28
37
|
<slot
|
|
29
38
|
:name="panel.type"
|
|
30
39
|
:panel="panel"
|
|
@@ -34,9 +43,9 @@
|
|
|
34
43
|
:close="() => closePanel(panel)"
|
|
35
44
|
:expand="() => expandPanel(panel)"
|
|
36
45
|
:fullsize="() => fullsizePanel(panel)">
|
|
37
|
-
<component
|
|
46
|
+
<component v-if="panel.components.default" :is="panel.components.default" :panel="panel" :payload="panel.payload" />
|
|
38
47
|
</slot>
|
|
39
|
-
<template v-if="$scopedSlots[`${panel.type}.title`] ||
|
|
48
|
+
<template v-if="$scopedSlots[`${panel.type}.title`] || panel.components.title" #title>
|
|
40
49
|
<slot
|
|
41
50
|
:name="`${panel.type}.title`"
|
|
42
51
|
:panel="panel"
|
|
@@ -46,10 +55,10 @@
|
|
|
46
55
|
:close="() => closePanel(panel)"
|
|
47
56
|
:expand="() => expandPanel(panel)"
|
|
48
57
|
:fullsize="() => fullsizePanel(panel)">
|
|
49
|
-
<component v-if="
|
|
58
|
+
<component v-if="panel.components.title" :is="panel.components.title" :panel="panel" :payload="panel.payload" />
|
|
50
59
|
</slot>
|
|
51
60
|
</template>
|
|
52
|
-
<template v-if="$scopedSlots[`${panel.type}.buttons`] ||
|
|
61
|
+
<template v-if="$scopedSlots[`${panel.type}.buttons`] || panel.components.buttons" #buttons>
|
|
53
62
|
<slot
|
|
54
63
|
:name="`${panel.type}.buttons`"
|
|
55
64
|
:panel="panel"
|
|
@@ -59,10 +68,10 @@
|
|
|
59
68
|
:close="() => closePanel(panel)"
|
|
60
69
|
:expand="() => expandPanel(panel)"
|
|
61
70
|
:fullsize="() => fullsizePanel(panel)">
|
|
62
|
-
<component v-if="
|
|
71
|
+
<component v-if="panel.components.buttons" :is="panel.components.buttons" :panel="panel" :payload="panel.payload" />
|
|
63
72
|
</slot>
|
|
64
73
|
</template>
|
|
65
|
-
<template v-if="$scopedSlots[`${panel.type}.header`] ||
|
|
74
|
+
<template v-if="$scopedSlots[`${panel.type}.header`] || panel.components.header" #header>
|
|
66
75
|
<slot
|
|
67
76
|
:name="`${panel.type}.header`"
|
|
68
77
|
:panel="panel"
|
|
@@ -72,7 +81,7 @@
|
|
|
72
81
|
:close="() => closePanel(panel)"
|
|
73
82
|
:expand="() => expandPanel(panel)"
|
|
74
83
|
:fullsize="() => fullsizePanel(panel)">
|
|
75
|
-
<component v-if="
|
|
84
|
+
<component v-if="panel.components.header" :is="panel.components.header" :panel="panel" :payload="panel.payload" />
|
|
76
85
|
</slot>
|
|
77
86
|
</template>
|
|
78
87
|
</panel>
|
|
@@ -148,7 +157,6 @@ $double-an-time: $an-time * 2;
|
|
|
148
157
|
//transition: opacity $an-time linear;
|
|
149
158
|
}
|
|
150
159
|
}
|
|
151
|
-
|
|
152
160
|
//.slide-enter-active > div {
|
|
153
161
|
// opacity: 0;
|
|
154
162
|
//}
|
|
@@ -158,9 +166,11 @@ $double-an-time: $an-time * 2;
|
|
|
158
166
|
//}
|
|
159
167
|
</style>
|
|
160
168
|
<script lang="ts">
|
|
161
|
-
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
162
|
-
import
|
|
163
|
-
import
|
|
169
|
+
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
|
|
170
|
+
import itfIcon from '../icon/Icon.vue';
|
|
171
|
+
import Panel from './Panel.vue';
|
|
172
|
+
import {hashToStack, stackToHash} from "@itfin/components/src/components/panels/helpers";
|
|
173
|
+
import {emitGlobalEvent, setRootPanelList} from "@itfin/components/src/components/panels";
|
|
164
174
|
|
|
165
175
|
interface VisualOptions {
|
|
166
176
|
title: string;
|
|
@@ -175,6 +185,7 @@ export interface IPanel {
|
|
|
175
185
|
payload: any;
|
|
176
186
|
nocard?: boolean;
|
|
177
187
|
isCollapsed: boolean;
|
|
188
|
+
isExpanded: boolean;
|
|
178
189
|
isCloseable: boolean;
|
|
179
190
|
isAnimate: boolean;
|
|
180
191
|
open: (type: string, visOptions: VisualOptions, payload: any) => void;
|
|
@@ -195,6 +206,7 @@ export interface IPanel {
|
|
|
195
206
|
|
|
196
207
|
@Component({
|
|
197
208
|
components: {
|
|
209
|
+
itfIcon,
|
|
198
210
|
Panel
|
|
199
211
|
},
|
|
200
212
|
directives: {
|
|
@@ -208,12 +220,26 @@ export interface IPanel {
|
|
|
208
220
|
export default class PanelList extends Vue {
|
|
209
221
|
@Prop() firstPanel: IPanel;
|
|
210
222
|
@Prop() panels: Record<string, Component>;
|
|
223
|
+
@Prop({ default: () => {} }) searchPanel: (type: string) => boolean;
|
|
224
|
+
@Prop({ type: String, default: 'path' }) routeType: string;
|
|
211
225
|
|
|
212
226
|
panelsStack:IPanel[] = [];
|
|
213
227
|
|
|
214
228
|
nextId:number = 0;
|
|
215
229
|
|
|
230
|
+
@Watch('panels', { deep: true })
|
|
231
|
+
onPanelsPropChanged(newPanels: Record<string, Component>) {
|
|
232
|
+
this.panelsStack.forEach(stackedPanel => {
|
|
233
|
+
const newPanel = newPanels[stackedPanel.type];
|
|
234
|
+
if (newPanel && typeof newPanel.inAppOnboarding === 'function') {
|
|
235
|
+
const updatedOnboarding = newPanel.inAppOnboarding(this.$t.bind(this));
|
|
236
|
+
stackedPanel.inAppOnboarding = updatedOnboarding;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
216
241
|
created() {
|
|
242
|
+
setRootPanelList(this);
|
|
217
243
|
if (this.firstPanel) {
|
|
218
244
|
this.internalOpenPanel(this.firstPanel.type, this.firstPanel.payload);
|
|
219
245
|
}
|
|
@@ -233,6 +259,7 @@ export default class PanelList extends Vue {
|
|
|
233
259
|
const newStack = [...this.panelsStack];
|
|
234
260
|
const index = newStack.findIndex(p => p.id === panel.id);
|
|
235
261
|
newStack[index].isCollapsed = false;
|
|
262
|
+
newStack[index].isExpanded = true;
|
|
236
263
|
this.panelsStack = newStack;
|
|
237
264
|
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
238
265
|
this.setPanelHash();
|
|
@@ -254,10 +281,12 @@ export default class PanelList extends Vue {
|
|
|
254
281
|
keepOpenIds.push(panel2.id);
|
|
255
282
|
}
|
|
256
283
|
if (keepOpenIds.length === 1) {
|
|
257
|
-
if (
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
284
|
+
if (!openPanels.find(p => p.id === keepOpenId).isExpanded) {
|
|
285
|
+
if (newStack.length - 1 === indexKeep) {
|
|
286
|
+
keepOpenIds.push(newStack[indexKeep - 1].id);
|
|
287
|
+
} else {
|
|
288
|
+
keepOpenIds.push(newStack[indexKeep + 1].id);
|
|
289
|
+
}
|
|
261
290
|
}
|
|
262
291
|
}
|
|
263
292
|
for (const panel of newStack) {
|
|
@@ -267,29 +296,42 @@ export default class PanelList extends Vue {
|
|
|
267
296
|
this.panelsStack = newStack;
|
|
268
297
|
}
|
|
269
298
|
|
|
270
|
-
internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false) {
|
|
271
|
-
|
|
272
|
-
|
|
299
|
+
async internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false, noExpand = false) {
|
|
300
|
+
let panel = this.panels[type];
|
|
301
|
+
if (!panel) {
|
|
302
|
+
panel = await this.searchPanel(type, this.panels);
|
|
303
|
+
if (!panel) {
|
|
304
|
+
console.error(`Panel type "${type}" not found`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
panel.type = type;
|
|
273
308
|
}
|
|
274
|
-
if (typeof
|
|
309
|
+
if (typeof panel.caption !== 'function') {
|
|
275
310
|
throw new Error('Panel component must have a "caption" function');
|
|
276
311
|
}
|
|
277
312
|
const newPanel:any = {
|
|
278
313
|
id: this.nextId++,
|
|
279
|
-
nocard:
|
|
280
|
-
title:
|
|
281
|
-
icon:
|
|
314
|
+
nocard: panel.nocard,
|
|
315
|
+
title: panel.caption(this.$t.bind(this), payload),
|
|
316
|
+
icon: panel.icon ? panel.icon(this.$t.bind(this), payload) : null,
|
|
317
|
+
components: {
|
|
318
|
+
default: panel.default ?? undefined,
|
|
319
|
+
buttons: panel.buttons ?? undefined,
|
|
320
|
+
header: panel.header ?? undefined,
|
|
321
|
+
title: panel.title ?? undefined,
|
|
322
|
+
},
|
|
282
323
|
type,
|
|
283
324
|
payload,
|
|
284
325
|
isCollapsed: false,
|
|
285
326
|
isCloseable: true,
|
|
286
327
|
__events: {},
|
|
328
|
+
inAppOnboarding: panel.inAppOnboarding ? panel.inAppOnboarding(this.$t.bind(this)) : null,
|
|
287
329
|
};
|
|
288
330
|
if (!this.panelsStack.length || openIndex === 0) {
|
|
289
331
|
newPanel.isCloseable = false;
|
|
290
332
|
}
|
|
291
333
|
let newStack = [...this.panelsStack];
|
|
292
|
-
if (
|
|
334
|
+
if (panel.permanentExpanded && newStack.length) {
|
|
293
335
|
for (const panel of newStack) {
|
|
294
336
|
panel.isCollapsed = true;
|
|
295
337
|
}
|
|
@@ -299,25 +341,32 @@ export default class PanelList extends Vue {
|
|
|
299
341
|
isAnimation = newStack.length === openIndex;
|
|
300
342
|
newStack = newStack.slice(0, openIndex);
|
|
301
343
|
}
|
|
344
|
+
if (newStack.length > 0 && !newStack.find(p => !p.isCollapsed) && !noExpand) {
|
|
345
|
+
// якщо немає відкритих панелей, то перша панель має бути розгорнута
|
|
346
|
+
newStack[0].isCollapsed = false;
|
|
347
|
+
}
|
|
302
348
|
this.panelsStack = newStack;
|
|
303
349
|
return new Promise(res => {
|
|
304
350
|
this.$nextTick(() => { // щоб панелі змінювались при редагуванні
|
|
305
351
|
const n = newStack.length;
|
|
306
352
|
newPanel.isAnimate = isAnimation;
|
|
307
|
-
newPanel.permanentExpanded = !!
|
|
353
|
+
newPanel.permanentExpanded = !!panel.permanentExpanded;
|
|
308
354
|
newPanel.emit = (event, ...args) => this.emitEvent(event, ...args);
|
|
309
|
-
newPanel.open = (type, payload) => this.openPanel(type, payload, n + 1);
|
|
355
|
+
newPanel.open = (type, payload, index?:number) => this.openPanel(type, payload, index ?? n + 1);
|
|
310
356
|
newPanel.close = () => this.closePanel(newPanel);
|
|
311
357
|
newPanel.expand = () => this.expandPanel(newPanel);
|
|
312
358
|
newPanel.getTitle = () => newPanel.title;
|
|
313
359
|
newPanel.getIcon = () => newPanel.icon;
|
|
314
|
-
newPanel.setTitle = (title: string) => { newPanel.title = title; };
|
|
360
|
+
newPanel.setTitle = (title: string) => { newPanel.title = title; this.updateTitle() };
|
|
315
361
|
newPanel.setIcon = (icon: string) => { newPanel.icon = icon; };
|
|
316
|
-
newPanel.on = (eventName, func: (event: string, ...args: any[]) => any) => {
|
|
317
|
-
|
|
318
|
-
|
|
362
|
+
newPanel.on = (eventName: string|string[], func: (event: string, ...args: any[]) => any) => {
|
|
363
|
+
const eventNames = Array.isArray(eventName) ? eventName : [eventName];
|
|
364
|
+
for (const evName of eventNames) {
|
|
365
|
+
if (!newPanel.__events[evName]) {
|
|
366
|
+
newPanel.__events[evName] = [];
|
|
367
|
+
}
|
|
368
|
+
newPanel.__events[evName].push(func);
|
|
319
369
|
}
|
|
320
|
-
newPanel.__events[eventName].push(func);
|
|
321
370
|
};
|
|
322
371
|
newPanel.off = (eventName, func: (event: string, ...args: any[]) => any) => {
|
|
323
372
|
if (newPanel.__events[eventName]) {
|
|
@@ -337,9 +386,11 @@ export default class PanelList extends Vue {
|
|
|
337
386
|
newPanel.getPayload = () => newPanel.payload;
|
|
338
387
|
newPanel.setPayload = (value: any) => {
|
|
339
388
|
newPanel.payload = value;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
389
|
+
this.setPanelHash();
|
|
390
|
+
}
|
|
391
|
+
newPanel.rebuildAllOnboardingPopovers = (refreshDelay?: number) => this.rebuildAllOnboardingPopovers(refreshDelay);
|
|
392
|
+
newPanel.rebuildPanelOnboardingPopovers = (panelId: string | number, refreshDelay?: number) => {
|
|
393
|
+
this.rebuildPanelOnboardingPopovers(panelId, refreshDelay);
|
|
343
394
|
}
|
|
344
395
|
newStack.push(newPanel);
|
|
345
396
|
this.panelsStack = newStack;
|
|
@@ -353,12 +404,20 @@ export default class PanelList extends Vue {
|
|
|
353
404
|
});
|
|
354
405
|
}
|
|
355
406
|
|
|
407
|
+
updateTitle() {
|
|
408
|
+
const titles = this.panelsStack.map(p => p.getTitle()).filter(Boolean).reverse();
|
|
409
|
+
this.$root.$options.head.titleChunk = titles.join(' / ');
|
|
410
|
+
this.$meta().refresh();
|
|
411
|
+
}
|
|
412
|
+
|
|
356
413
|
async openPanel(type: string, payload: any, openIndex?: number) {
|
|
357
414
|
await this.internalOpenPanel(type, payload, openIndex);
|
|
358
|
-
this.setPanelHash()
|
|
415
|
+
this.setPanelHash();
|
|
416
|
+
if(openIndex) this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
359
417
|
}
|
|
360
418
|
|
|
361
419
|
emitEvent(event: string, ...args: any[]) {
|
|
420
|
+
emitGlobalEvent(event, ...args);
|
|
362
421
|
for (const panel of this.panelsStack) {
|
|
363
422
|
if (panel.__events[event]) {
|
|
364
423
|
for (const func of panel.__events[event]) {
|
|
@@ -376,22 +435,63 @@ export default class PanelList extends Vue {
|
|
|
376
435
|
openPanel.isCollapsed = false;
|
|
377
436
|
}
|
|
378
437
|
const openPanelIndex = this.panelsStack.findIndex(p => p === openPanel);
|
|
379
|
-
if (openPanelIndex > 0 && !openPanel?.permanentExpanded) {
|
|
438
|
+
if (openPanelIndex > 0 && !openPanel?.permanentExpanded && !openPanel.isExpanded) {
|
|
380
439
|
this.panelsStack[openPanelIndex - 1].isCollapsed = false;
|
|
381
440
|
}
|
|
382
441
|
this.ensureOnlyTwoOpenPanels(openPanel.id);
|
|
383
442
|
this.setPanelHash();
|
|
384
443
|
this.emitEvent('panels.closed', panel);
|
|
385
444
|
this.emitEvent('panels.changed', this.panelsStack);
|
|
445
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
386
446
|
}
|
|
387
447
|
|
|
388
448
|
fullsizePanel(panel: IPanel) {
|
|
389
449
|
const newStack = [...this.panelsStack];
|
|
390
450
|
for (const p of newStack) {
|
|
451
|
+
p.isLastOpened = !p.isCollapsed && p !== panel;
|
|
391
452
|
p.isCollapsed = p !== panel;
|
|
453
|
+
if (!p.isCollapsed) {
|
|
454
|
+
p.isExpanded = true;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
this.panelsStack = newStack;
|
|
458
|
+
this.setPanelHash();
|
|
459
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
get isFullSize() {
|
|
463
|
+
return this.panelsStack.filter(p => !p.isCollapsed).length === 1;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
expandPanel(panel: IPanel) {
|
|
467
|
+
const newStack = [...this.panelsStack];
|
|
468
|
+
const index = newStack.findIndex(p => p.id === panel.id);
|
|
469
|
+
newStack[index].isCollapsed = false;
|
|
470
|
+
for (const p of newStack) {
|
|
471
|
+
p.isExpanded = false;
|
|
472
|
+
}
|
|
473
|
+
this.panelsStack = newStack;
|
|
474
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
475
|
+
this.setPanelHash();
|
|
476
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
collapsePanel(panel: IPanel) {
|
|
480
|
+
const newStack = [...this.panelsStack];
|
|
481
|
+
const currenctIndex = newStack.findIndex(p => p.id === panel.id);
|
|
482
|
+
const lastOpenedIndex = newStack.findIndex(p => p.isLastOpened);
|
|
483
|
+
if (lastOpenedIndex !== -1) { // якщо зебрежена остання відкрита панель
|
|
484
|
+
newStack[lastOpenedIndex].isCollapsed = false;
|
|
485
|
+
} else if (newStack[currenctIndex-1]) { // якщо після оновлення сторінки відсутнє значення "остання відкрита", то відкриваємо ту, що зліва
|
|
486
|
+
newStack[currenctIndex-1].isCollapsed = false;
|
|
487
|
+
}
|
|
488
|
+
for (const p of newStack) {
|
|
489
|
+
p.isExpanded = false;
|
|
392
490
|
}
|
|
393
491
|
this.panelsStack = newStack;
|
|
492
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
394
493
|
this.setPanelHash();
|
|
494
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
395
495
|
}
|
|
396
496
|
|
|
397
497
|
getPanels(type) {
|
|
@@ -403,14 +503,19 @@ export default class PanelList extends Vue {
|
|
|
403
503
|
}
|
|
404
504
|
|
|
405
505
|
setPanelHash() {
|
|
406
|
-
const hash = stackToHash(this.panelsStack).replace(/^#/, '');
|
|
407
|
-
this
|
|
506
|
+
const hash = stackToHash(this.panelsStack, this.routeType === 'path').replace(/^#/, '');
|
|
507
|
+
if (this.routeType === 'path') {
|
|
508
|
+
this.$router.push({ path: `${hash}` });
|
|
509
|
+
} else {
|
|
510
|
+
this.$router.push({ hash });
|
|
511
|
+
}
|
|
512
|
+
this.updateTitle();
|
|
408
513
|
}
|
|
409
514
|
|
|
410
515
|
async parsePanelHash() {
|
|
411
|
-
const
|
|
516
|
+
const hash = this.routeType === 'path' ? location.pathname : location.hash;
|
|
412
517
|
if (hash) {
|
|
413
|
-
const panels = hashToStack(hash);
|
|
518
|
+
const panels = hashToStack(hash, this.routeType === 'path');
|
|
414
519
|
const newStack = [];
|
|
415
520
|
this.panelsStack = [];
|
|
416
521
|
for (const panelIndex in panels) {
|
|
@@ -419,18 +524,25 @@ export default class PanelList extends Vue {
|
|
|
419
524
|
// reuse panel
|
|
420
525
|
this.panelsStack[panelIndex].payload = panel.payload;
|
|
421
526
|
this.panelsStack[panelIndex].isCollapsed = panel.isCollapsed;
|
|
527
|
+
this.panelsStack[panelIndex].isExpanded = false;
|
|
422
528
|
newStack.push(this.panelsStack[panelIndex]);
|
|
423
529
|
} else {
|
|
424
|
-
const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true);
|
|
530
|
+
const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true, true);
|
|
425
531
|
if (resPanel) {
|
|
426
532
|
resPanel.isCollapsed = panel.isCollapsed;
|
|
533
|
+
resPanel.isExpanded = false;
|
|
427
534
|
resPanel.isAnimate = false;
|
|
428
535
|
newStack.push(resPanel);
|
|
429
536
|
}
|
|
430
537
|
}
|
|
431
538
|
}
|
|
539
|
+
const hasExpanded = newStack.length > 1 && newStack.filter(p => !p.isCollapsed).length === 1;
|
|
540
|
+
if (hasExpanded) {
|
|
541
|
+
newStack[newStack.findIndex(p => !p.isCollapsed)].isExpanded = true;
|
|
542
|
+
}
|
|
432
543
|
this.panelsStack = newStack;
|
|
433
544
|
this.emitEvent('panels.changed', this.panelsStack);
|
|
545
|
+
this.updateTitle();
|
|
434
546
|
}
|
|
435
547
|
}
|
|
436
548
|
|
|
@@ -452,5 +564,16 @@ export default class PanelList extends Vue {
|
|
|
452
564
|
element.classList.add('animate');
|
|
453
565
|
}
|
|
454
566
|
}
|
|
567
|
+
|
|
568
|
+
rebuildPanelOnboardingPopovers(panelId: string | number, refreshDelay?: number) {
|
|
569
|
+
const panelRef = this.$refs[`panel-${panelId}`];
|
|
570
|
+
if (panelRef?.[0]) panelRef[0].rebuildOnboardingPopovers(refreshDelay);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
rebuildAllOnboardingPopovers(refreshDelay?: number) {
|
|
574
|
+
this.panelsStack.forEach((panel) => {
|
|
575
|
+
this.rebuildPanelOnboardingPopovers(panel.id, refreshDelay);
|
|
576
|
+
});
|
|
577
|
+
}
|
|
455
578
|
}
|
|
456
579
|
</script>
|
|
@@ -1,33 +1,51 @@
|
|
|
1
|
+
import JSON5 from 'json5'
|
|
2
|
+
import {isPathType} from "./index";
|
|
3
|
+
|
|
1
4
|
export interface IPanel {
|
|
2
5
|
type: string;
|
|
3
6
|
payload?: any;
|
|
4
7
|
isCollapsed?: boolean;
|
|
5
8
|
}
|
|
6
9
|
|
|
10
|
+
const COLLAPSE_SYMBOL = '~'
|
|
11
|
+
const PARAMS_SYMBOL = ';'
|
|
12
|
+
|
|
7
13
|
export function stackToHash(stack: IPanel[]) {
|
|
8
14
|
const hash = stack.map(panel => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
let json = JSON5.stringify(panel.payload || {});
|
|
16
|
+
json = json.substring(1, json.length - 1); // Remove the outer {}
|
|
17
|
+
return `${panel.type}${panel.isCollapsed ? COLLAPSE_SYMBOL : ''}${json ? PARAMS_SYMBOL : ''}${json}`;
|
|
18
|
+
}).join(isPathType() ? '/' : '&');
|
|
19
|
+
return isPathType() ? `/${hash}` : `#${hash}`;
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
|
|
15
23
|
export function hashToStack(hash: string|undefined): IPanel[] {
|
|
16
24
|
let stack:IPanel[] = [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
const str = hash.replace(isPathType() ? /^\// : /^#/, '');
|
|
26
|
+
if (str) {
|
|
27
|
+
stack = str.split(isPathType() ? '/' : '&').map(item => {
|
|
28
|
+
if (!item.includes(PARAMS_SYMBOL)) {
|
|
29
|
+
return { type: item.replace(COLLAPSE_SYMBOL, ''), isCollapsed: item.includes(COLLAPSE_SYMBOL), payload: {} };
|
|
30
|
+
}
|
|
31
|
+
const [type, payload] = item.split(PARAMS_SYMBOL);
|
|
32
|
+
const isCollapsed = type.includes(COLLAPSE_SYMBOL);
|
|
23
33
|
let payloadObj:any = {};
|
|
24
34
|
try {
|
|
25
|
-
|
|
35
|
+
let json = decodeURIComponent(payload);
|
|
36
|
+
if (!json.startsWith('{')) {
|
|
37
|
+
json = `{${json}`; // Ensure it starts with a '{' to be valid JSON
|
|
38
|
+
}
|
|
39
|
+
if (!json.endsWith('}')) {
|
|
40
|
+
json += '}'; // Ensure it ends with a '}' to be valid JSON
|
|
41
|
+
}
|
|
42
|
+
payloadObj = JSON5.parse(json);
|
|
26
43
|
} catch (e) {
|
|
27
44
|
// ignore
|
|
45
|
+
console.warn(`Error parsing payload for type ${type}:`, payload, e);
|
|
28
46
|
}
|
|
29
47
|
return {
|
|
30
|
-
type: type.replace(
|
|
48
|
+
type: type.replace(COLLAPSE_SYMBOL, ''),
|
|
31
49
|
isCollapsed,
|
|
32
50
|
payload: payloadObj
|
|
33
51
|
};
|
|
@@ -24,20 +24,26 @@ export default @Component({
|
|
|
24
24
|
class itfPopover extends Vue {
|
|
25
25
|
@PropSync('visible') value;
|
|
26
26
|
|
|
27
|
-
@Prop({ type: String, default: 'bottom', validator: (value) => ['bottom', 'left', 'right', 'top'].includes(value) }) placement;
|
|
27
|
+
@Prop({ type: String, default: 'bottom', validator: (value) => ['bottom', 'left', 'right', 'top', 'auto'].includes(value) }) placement;
|
|
28
28
|
|
|
29
29
|
@Prop({ type: String, default: 'click', validator: (value) => ['click', 'focus', 'hover', 'manual'].includes(value) }) trigger;
|
|
30
30
|
|
|
31
31
|
@Prop({ type: String, default: '' }) customClass;
|
|
32
|
+
@Prop({ type: Boolean }) appendToBody;
|
|
33
|
+
@Prop({ type: Boolean }) appendToContext;
|
|
34
|
+
@Prop({ type: String }) locator;
|
|
35
|
+
@Prop({ type: Boolean, default: false }) isActivatorHighlighted;
|
|
32
36
|
|
|
33
37
|
modalId = '';
|
|
34
|
-
|
|
35
38
|
modalEl = null;
|
|
39
|
+
targetEl = null;
|
|
36
40
|
|
|
37
41
|
ignoreUpdate = false; // потрібно щоб не оновлювалось visible декілька разів, бо буде баг коли вікно відкривається і одразу закривається
|
|
38
42
|
|
|
43
|
+
initTimeout = null;
|
|
44
|
+
|
|
39
45
|
beforeDestroy() {
|
|
40
|
-
this.
|
|
46
|
+
this.destroy();
|
|
41
47
|
}
|
|
42
48
|
|
|
43
49
|
@Watch('value')
|
|
@@ -46,9 +52,26 @@ class itfPopover extends Vue {
|
|
|
46
52
|
return;
|
|
47
53
|
}
|
|
48
54
|
if (newValue) {
|
|
49
|
-
this.modalEl
|
|
55
|
+
this.modalEl?.show();
|
|
56
|
+
} else {
|
|
57
|
+
this.modalEl?.hide();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Watch('locator')
|
|
62
|
+
onLocatorChanged(newValue, oldValue) {
|
|
63
|
+
if (newValue !== oldValue && newValue && this.modalEl) {
|
|
64
|
+
this.initPopover();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Watch('isActivatorHighlighted')
|
|
69
|
+
onIsActivatorHighlightedChanged(newValue, oldValue) {
|
|
70
|
+
if (!this.targetEl) return;
|
|
71
|
+
if (newValue) {
|
|
72
|
+
this.targetEl.classList.add('pulsing');
|
|
50
73
|
} else {
|
|
51
|
-
this.
|
|
74
|
+
this.targetEl.classList.remove('pulsing');
|
|
52
75
|
}
|
|
53
76
|
}
|
|
54
77
|
|
|
@@ -58,14 +81,62 @@ class itfPopover extends Vue {
|
|
|
58
81
|
}
|
|
59
82
|
|
|
60
83
|
async mounted() {
|
|
84
|
+
this.refresh(100);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
destroy() {
|
|
88
|
+
if (this.initTimeout) {
|
|
89
|
+
clearTimeout(this.initTimeout);
|
|
90
|
+
this.initTimeout = null;
|
|
91
|
+
}
|
|
92
|
+
if (this.modalEl) {
|
|
93
|
+
this.modalEl.dispose();
|
|
94
|
+
this.modalEl = null;
|
|
95
|
+
}
|
|
96
|
+
if (this.targetEl) this.resetTargetEl();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
refresh(delay = 300) {
|
|
100
|
+
this.destroy();
|
|
101
|
+
|
|
102
|
+
if (this.initTimeout) clearTimeout(this.initTimeout);
|
|
103
|
+
this.initTimeout = setTimeout(() => this.initPopover(), delay);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async initPopover() {
|
|
61
107
|
if (typeof window === 'undefined') {
|
|
62
108
|
return;
|
|
63
109
|
}
|
|
64
|
-
|
|
110
|
+
|
|
111
|
+
// Remove listeners from the previous target element before finding the new one
|
|
112
|
+
if (this.modalEl) this.destroy();
|
|
113
|
+
|
|
114
|
+
// Determine target
|
|
115
|
+
if (this.locator) {
|
|
116
|
+
this.targetEl = document.querySelector(this.locator);
|
|
117
|
+
if (!this.targetEl) return;
|
|
118
|
+
} else {
|
|
119
|
+
this.targetEl = this.$el;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this.isActivatorHighlighted) this.targetEl.classList.add('pulsing');
|
|
123
|
+
|
|
124
|
+
const el = this.$refs.popover || "";
|
|
65
125
|
const { default: Popover } = await import('bootstrap/js/src/popover');
|
|
66
126
|
|
|
67
|
-
|
|
68
|
-
|
|
127
|
+
if (this._isDestroyed) return;
|
|
128
|
+
|
|
129
|
+
let context = document.body;
|
|
130
|
+
if (this.appendToContext && this.targetEl instanceof Node && this.targetEl.parentNode) {
|
|
131
|
+
context = this.targetEl.closest('.itf-append-context') || document.body;
|
|
132
|
+
this.targetEl.parentNode.removeChild(this.targetEl);
|
|
133
|
+
context.appendChild(this.targetEl); // should append only to body
|
|
134
|
+
} else if (this.appendToBody && this.targetEl instanceof Node && this.targetEl.parentNode) {
|
|
135
|
+
this.targetEl.parentNode.removeChild(this.targetEl);
|
|
136
|
+
context.appendChild(this.targetEl); // should append only to body
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.modalEl = new Popover(this.targetEl, {
|
|
69
140
|
content: el,
|
|
70
141
|
container: context,
|
|
71
142
|
sanitize: false,
|
|
@@ -74,26 +145,38 @@ class itfPopover extends Vue {
|
|
|
74
145
|
placement: this.placement,
|
|
75
146
|
trigger: this.trigger,
|
|
76
147
|
});
|
|
148
|
+
|
|
77
149
|
this.onVisibleChanged(this.value);
|
|
78
|
-
this
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
this
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
150
|
+
this.targetEl.addEventListener('shown.bs.popover', this.handleShown);
|
|
151
|
+
this.targetEl.addEventListener('hidden.bs.popover', this.handleHidden);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
resetTargetEl() {
|
|
155
|
+
this.targetEl.removeEventListener('shown.bs.popover', this.handleShown);
|
|
156
|
+
this.targetEl.removeEventListener('hidden.bs.popover', this.handleHidden);
|
|
157
|
+
this.targetEl.classList.remove('pulsing');
|
|
158
|
+
this.targetEl = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
handleShown() {
|
|
162
|
+
this.ignoreUpdate = true;
|
|
163
|
+
this.value = true;
|
|
164
|
+
document.body.addEventListener('click', this.clickOutside);
|
|
165
|
+
this.$nextTick(() => this.ignoreUpdate = false);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
handleHidden() {
|
|
169
|
+
this.ignoreUpdate = true;
|
|
170
|
+
this.value = false;
|
|
171
|
+
document.body.removeEventListener('click', this.clickOutside);
|
|
172
|
+
this.$nextTick(() => this.ignoreUpdate = false);
|
|
90
173
|
}
|
|
91
174
|
|
|
92
175
|
clickOutside(e) {
|
|
93
176
|
if (this.trigger !== 'click' || !e.target) {
|
|
94
177
|
return;
|
|
95
178
|
}
|
|
96
|
-
if (this
|
|
179
|
+
if (this.targetEl !== e.target && e.target.closest('.itf-popover') !== this.$refs.popover) {
|
|
97
180
|
this.hide();
|
|
98
181
|
}
|
|
99
182
|
}
|
|
@@ -107,7 +190,7 @@ class itfPopover extends Vue {
|
|
|
107
190
|
hide() {
|
|
108
191
|
if (this.modalEl) {
|
|
109
192
|
// треба затримка щоб попав не показався знову через отримання фокусу
|
|
110
|
-
setTimeout(() => this.modalEl
|
|
193
|
+
setTimeout(() => this.modalEl?.hide(), 10);
|
|
111
194
|
}
|
|
112
195
|
}
|
|
113
196
|
}
|