@itfin/components 1.4.40 → 1.5.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/package.json +17 -20
- package/src/ITFSettings.js +6 -0
- package/src/assets/scss/components/_button.scss +118 -0
- package/src/assets/scss/components/_text-field.scss +22 -8
- 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/editor/plugins.js +1012 -0
- package/src/components/filter/FilterBadge.vue +17 -3
- package/src/components/filter/FilterFacetsList.vue +67 -13
- package/src/components/filter/FilterPanel.vue +8 -21
- 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-bell.vue +5 -0
- package/src/components/icon/components/nomi-budget.vue +4 -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-category-edit.vue +5 -0
- package/src/components/icon/components/nomi-delta.vue +7 -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-lock.vue +1 -1
- 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/delta.svg +6 -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 +164 -40
- package/src/components/panels/helpers.ts +41 -13
- 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 +80 -207
- 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,27 @@ 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;
|
|
225
|
+
@Prop({ type: String, default: '' }) routePrefix: string;
|
|
211
226
|
|
|
212
227
|
panelsStack:IPanel[] = [];
|
|
213
228
|
|
|
214
229
|
nextId:number = 0;
|
|
215
230
|
|
|
231
|
+
@Watch('panels', { deep: true })
|
|
232
|
+
onPanelsPropChanged(newPanels: Record<string, Component>) {
|
|
233
|
+
this.panelsStack.forEach(stackedPanel => {
|
|
234
|
+
const newPanel = newPanels[stackedPanel.type];
|
|
235
|
+
if (newPanel && typeof newPanel.inAppOnboarding === 'function') {
|
|
236
|
+
const updatedOnboarding = newPanel.inAppOnboarding(this.$t.bind(this));
|
|
237
|
+
stackedPanel.inAppOnboarding = updatedOnboarding;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
216
242
|
created() {
|
|
243
|
+
setRootPanelList(this);
|
|
217
244
|
if (this.firstPanel) {
|
|
218
245
|
this.internalOpenPanel(this.firstPanel.type, this.firstPanel.payload);
|
|
219
246
|
}
|
|
@@ -233,6 +260,7 @@ export default class PanelList extends Vue {
|
|
|
233
260
|
const newStack = [...this.panelsStack];
|
|
234
261
|
const index = newStack.findIndex(p => p.id === panel.id);
|
|
235
262
|
newStack[index].isCollapsed = false;
|
|
263
|
+
newStack[index].isExpanded = true;
|
|
236
264
|
this.panelsStack = newStack;
|
|
237
265
|
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
238
266
|
this.setPanelHash();
|
|
@@ -254,10 +282,12 @@ export default class PanelList extends Vue {
|
|
|
254
282
|
keepOpenIds.push(panel2.id);
|
|
255
283
|
}
|
|
256
284
|
if (keepOpenIds.length === 1) {
|
|
257
|
-
if (
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
285
|
+
if (!openPanels.find(p => p.id === keepOpenId).isExpanded) {
|
|
286
|
+
if (newStack.length - 1 === indexKeep) {
|
|
287
|
+
keepOpenIds.push(newStack[indexKeep - 1].id);
|
|
288
|
+
} else {
|
|
289
|
+
keepOpenIds.push(newStack[indexKeep + 1].id);
|
|
290
|
+
}
|
|
261
291
|
}
|
|
262
292
|
}
|
|
263
293
|
for (const panel of newStack) {
|
|
@@ -267,29 +297,42 @@ export default class PanelList extends Vue {
|
|
|
267
297
|
this.panelsStack = newStack;
|
|
268
298
|
}
|
|
269
299
|
|
|
270
|
-
internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false) {
|
|
271
|
-
|
|
272
|
-
|
|
300
|
+
async internalOpenPanel(type: string, payload: any = {}, openIndex?: number, noEvents = false, noExpand = false) {
|
|
301
|
+
let panel = this.panels[type];
|
|
302
|
+
if (!panel) {
|
|
303
|
+
panel = await this.searchPanel(type, this.panels);
|
|
304
|
+
if (!panel) {
|
|
305
|
+
console.error(`Panel type "${type}" not found`);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
panel.type = type;
|
|
273
309
|
}
|
|
274
|
-
if (typeof
|
|
310
|
+
if (typeof panel.caption !== 'function') {
|
|
275
311
|
throw new Error('Panel component must have a "caption" function');
|
|
276
312
|
}
|
|
277
313
|
const newPanel:any = {
|
|
278
314
|
id: this.nextId++,
|
|
279
|
-
nocard:
|
|
280
|
-
title:
|
|
281
|
-
icon:
|
|
315
|
+
nocard: panel.nocard,
|
|
316
|
+
title: panel.caption(this.$t.bind(this), payload),
|
|
317
|
+
icon: panel.icon ? panel.icon(this.$t.bind(this), payload) : null,
|
|
318
|
+
components: {
|
|
319
|
+
default: panel.default ?? undefined,
|
|
320
|
+
buttons: panel.buttons ?? undefined,
|
|
321
|
+
header: panel.header ?? undefined,
|
|
322
|
+
title: panel.title ?? undefined,
|
|
323
|
+
},
|
|
282
324
|
type,
|
|
283
325
|
payload,
|
|
284
326
|
isCollapsed: false,
|
|
285
327
|
isCloseable: true,
|
|
286
328
|
__events: {},
|
|
329
|
+
inAppOnboarding: panel.inAppOnboarding ? panel.inAppOnboarding(this.$t.bind(this)) : null,
|
|
287
330
|
};
|
|
288
331
|
if (!this.panelsStack.length || openIndex === 0) {
|
|
289
332
|
newPanel.isCloseable = false;
|
|
290
333
|
}
|
|
291
334
|
let newStack = [...this.panelsStack];
|
|
292
|
-
if (
|
|
335
|
+
if (panel.permanentExpanded && newStack.length) {
|
|
293
336
|
for (const panel of newStack) {
|
|
294
337
|
panel.isCollapsed = true;
|
|
295
338
|
}
|
|
@@ -299,25 +342,32 @@ export default class PanelList extends Vue {
|
|
|
299
342
|
isAnimation = newStack.length === openIndex;
|
|
300
343
|
newStack = newStack.slice(0, openIndex);
|
|
301
344
|
}
|
|
345
|
+
if (newStack.length > 0 && !newStack.find(p => !p.isCollapsed) && !noExpand) {
|
|
346
|
+
// якщо немає відкритих панелей, то перша панель має бути розгорнута
|
|
347
|
+
newStack[0].isCollapsed = false;
|
|
348
|
+
}
|
|
302
349
|
this.panelsStack = newStack;
|
|
303
350
|
return new Promise(res => {
|
|
304
351
|
this.$nextTick(() => { // щоб панелі змінювались при редагуванні
|
|
305
352
|
const n = newStack.length;
|
|
306
353
|
newPanel.isAnimate = isAnimation;
|
|
307
|
-
newPanel.permanentExpanded = !!
|
|
354
|
+
newPanel.permanentExpanded = !!panel.permanentExpanded;
|
|
308
355
|
newPanel.emit = (event, ...args) => this.emitEvent(event, ...args);
|
|
309
|
-
newPanel.open = (type, payload) => this.openPanel(type, payload, n + 1);
|
|
356
|
+
newPanel.open = (type, payload, index?:number) => this.openPanel(type, payload, index ?? n + 1);
|
|
310
357
|
newPanel.close = () => this.closePanel(newPanel);
|
|
311
358
|
newPanel.expand = () => this.expandPanel(newPanel);
|
|
312
359
|
newPanel.getTitle = () => newPanel.title;
|
|
313
360
|
newPanel.getIcon = () => newPanel.icon;
|
|
314
|
-
newPanel.setTitle = (title: string) => { newPanel.title = title; };
|
|
361
|
+
newPanel.setTitle = (title: string) => { newPanel.title = title; this.updateTitle() };
|
|
315
362
|
newPanel.setIcon = (icon: string) => { newPanel.icon = icon; };
|
|
316
|
-
newPanel.on = (eventName, func: (event: string, ...args: any[]) => any) => {
|
|
317
|
-
|
|
318
|
-
|
|
363
|
+
newPanel.on = (eventName: string|string[], func: (event: string, ...args: any[]) => any) => {
|
|
364
|
+
const eventNames = Array.isArray(eventName) ? eventName : [eventName];
|
|
365
|
+
for (const evName of eventNames) {
|
|
366
|
+
if (!newPanel.__events[evName]) {
|
|
367
|
+
newPanel.__events[evName] = [];
|
|
368
|
+
}
|
|
369
|
+
newPanel.__events[evName].push(func);
|
|
319
370
|
}
|
|
320
|
-
newPanel.__events[eventName].push(func);
|
|
321
371
|
};
|
|
322
372
|
newPanel.off = (eventName, func: (event: string, ...args: any[]) => any) => {
|
|
323
373
|
if (newPanel.__events[eventName]) {
|
|
@@ -337,9 +387,11 @@ export default class PanelList extends Vue {
|
|
|
337
387
|
newPanel.getPayload = () => newPanel.payload;
|
|
338
388
|
newPanel.setPayload = (value: any) => {
|
|
339
389
|
newPanel.payload = value;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
390
|
+
this.setPanelHash();
|
|
391
|
+
}
|
|
392
|
+
newPanel.rebuildAllOnboardingPopovers = (refreshDelay?: number) => this.rebuildAllOnboardingPopovers(refreshDelay);
|
|
393
|
+
newPanel.rebuildPanelOnboardingPopovers = (panelId: string | number, refreshDelay?: number) => {
|
|
394
|
+
this.rebuildPanelOnboardingPopovers(panelId, refreshDelay);
|
|
343
395
|
}
|
|
344
396
|
newStack.push(newPanel);
|
|
345
397
|
this.panelsStack = newStack;
|
|
@@ -353,12 +405,20 @@ export default class PanelList extends Vue {
|
|
|
353
405
|
});
|
|
354
406
|
}
|
|
355
407
|
|
|
408
|
+
updateTitle() {
|
|
409
|
+
const titles = this.panelsStack.map(p => p.getTitle()).filter(Boolean).reverse();
|
|
410
|
+
this.$root.$options.head.titleChunk = titles.join(' / ');
|
|
411
|
+
this.$meta().refresh();
|
|
412
|
+
}
|
|
413
|
+
|
|
356
414
|
async openPanel(type: string, payload: any, openIndex?: number) {
|
|
357
415
|
await this.internalOpenPanel(type, payload, openIndex);
|
|
358
|
-
this.setPanelHash()
|
|
416
|
+
this.setPanelHash();
|
|
417
|
+
if(openIndex) this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
359
418
|
}
|
|
360
419
|
|
|
361
420
|
emitEvent(event: string, ...args: any[]) {
|
|
421
|
+
emitGlobalEvent(event, ...args);
|
|
362
422
|
for (const panel of this.panelsStack) {
|
|
363
423
|
if (panel.__events[event]) {
|
|
364
424
|
for (const func of panel.__events[event]) {
|
|
@@ -376,22 +436,63 @@ export default class PanelList extends Vue {
|
|
|
376
436
|
openPanel.isCollapsed = false;
|
|
377
437
|
}
|
|
378
438
|
const openPanelIndex = this.panelsStack.findIndex(p => p === openPanel);
|
|
379
|
-
if (openPanelIndex > 0 && !openPanel?.permanentExpanded) {
|
|
439
|
+
if (openPanelIndex > 0 && !openPanel?.permanentExpanded && !openPanel.isExpanded) {
|
|
380
440
|
this.panelsStack[openPanelIndex - 1].isCollapsed = false;
|
|
381
441
|
}
|
|
382
442
|
this.ensureOnlyTwoOpenPanels(openPanel.id);
|
|
383
443
|
this.setPanelHash();
|
|
384
444
|
this.emitEvent('panels.closed', panel);
|
|
385
445
|
this.emitEvent('panels.changed', this.panelsStack);
|
|
446
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
386
447
|
}
|
|
387
448
|
|
|
388
449
|
fullsizePanel(panel: IPanel) {
|
|
389
450
|
const newStack = [...this.panelsStack];
|
|
390
451
|
for (const p of newStack) {
|
|
452
|
+
p.isLastOpened = !p.isCollapsed && p !== panel;
|
|
391
453
|
p.isCollapsed = p !== panel;
|
|
454
|
+
if (!p.isCollapsed) {
|
|
455
|
+
p.isExpanded = true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
this.panelsStack = newStack;
|
|
459
|
+
this.setPanelHash();
|
|
460
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
get isFullSize() {
|
|
464
|
+
return this.panelsStack.filter(p => !p.isCollapsed).length === 1;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
expandPanel(panel: IPanel) {
|
|
468
|
+
const newStack = [...this.panelsStack];
|
|
469
|
+
const index = newStack.findIndex(p => p.id === panel.id);
|
|
470
|
+
newStack[index].isCollapsed = false;
|
|
471
|
+
for (const p of newStack) {
|
|
472
|
+
p.isExpanded = false;
|
|
473
|
+
}
|
|
474
|
+
this.panelsStack = newStack;
|
|
475
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
476
|
+
this.setPanelHash();
|
|
477
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
collapsePanel(panel: IPanel) {
|
|
481
|
+
const newStack = [...this.panelsStack];
|
|
482
|
+
const currenctIndex = newStack.findIndex(p => p.id === panel.id);
|
|
483
|
+
const lastOpenedIndex = newStack.findIndex(p => p.isLastOpened);
|
|
484
|
+
if (lastOpenedIndex !== -1) { // якщо зебрежена остання відкрита панель
|
|
485
|
+
newStack[lastOpenedIndex].isCollapsed = false;
|
|
486
|
+
} else if (newStack[currenctIndex-1]) { // якщо після оновлення сторінки відсутнє значення "остання відкрита", то відкриваємо ту, що зліва
|
|
487
|
+
newStack[currenctIndex-1].isCollapsed = false;
|
|
488
|
+
}
|
|
489
|
+
for (const p of newStack) {
|
|
490
|
+
p.isExpanded = false;
|
|
392
491
|
}
|
|
393
492
|
this.panelsStack = newStack;
|
|
493
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
394
494
|
this.setPanelHash();
|
|
495
|
+
this.$nextTick(() => { this.rebuildAllOnboardingPopovers() });
|
|
395
496
|
}
|
|
396
497
|
|
|
397
498
|
getPanels(type) {
|
|
@@ -403,14 +504,19 @@ export default class PanelList extends Vue {
|
|
|
403
504
|
}
|
|
404
505
|
|
|
405
506
|
setPanelHash() {
|
|
406
|
-
const hash = stackToHash(this.panelsStack).replace(/^#/, '');
|
|
407
|
-
this
|
|
507
|
+
const hash = stackToHash(this.panelsStack, this.routePrefix).replace(/^#/, '');
|
|
508
|
+
if (this.routeType === 'path') {
|
|
509
|
+
this.$router.push({ path: `${hash}` });
|
|
510
|
+
} else {
|
|
511
|
+
this.$router.push({ hash });
|
|
512
|
+
}
|
|
513
|
+
this.updateTitle();
|
|
408
514
|
}
|
|
409
515
|
|
|
410
516
|
async parsePanelHash() {
|
|
411
|
-
const
|
|
517
|
+
const hash = this.routeType === 'path' ? location.pathname : location.hash;
|
|
412
518
|
if (hash) {
|
|
413
|
-
const panels = hashToStack(hash);
|
|
519
|
+
const panels = hashToStack(hash, this.routePrefix);
|
|
414
520
|
const newStack = [];
|
|
415
521
|
this.panelsStack = [];
|
|
416
522
|
for (const panelIndex in panels) {
|
|
@@ -419,18 +525,25 @@ export default class PanelList extends Vue {
|
|
|
419
525
|
// reuse panel
|
|
420
526
|
this.panelsStack[panelIndex].payload = panel.payload;
|
|
421
527
|
this.panelsStack[panelIndex].isCollapsed = panel.isCollapsed;
|
|
528
|
+
this.panelsStack[panelIndex].isExpanded = false;
|
|
422
529
|
newStack.push(this.panelsStack[panelIndex]);
|
|
423
530
|
} else {
|
|
424
|
-
const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true);
|
|
531
|
+
const resPanel = await this.internalOpenPanel(panel.type, panel.payload, undefined, true, true);
|
|
425
532
|
if (resPanel) {
|
|
426
533
|
resPanel.isCollapsed = panel.isCollapsed;
|
|
534
|
+
resPanel.isExpanded = false;
|
|
427
535
|
resPanel.isAnimate = false;
|
|
428
536
|
newStack.push(resPanel);
|
|
429
537
|
}
|
|
430
538
|
}
|
|
431
539
|
}
|
|
540
|
+
const hasExpanded = newStack.length > 1 && newStack.filter(p => !p.isCollapsed).length === 1;
|
|
541
|
+
if (hasExpanded) {
|
|
542
|
+
newStack[newStack.findIndex(p => !p.isCollapsed)].isExpanded = true;
|
|
543
|
+
}
|
|
432
544
|
this.panelsStack = newStack;
|
|
433
545
|
this.emitEvent('panels.changed', this.panelsStack);
|
|
546
|
+
this.updateTitle();
|
|
434
547
|
}
|
|
435
548
|
}
|
|
436
549
|
|
|
@@ -452,5 +565,16 @@ export default class PanelList extends Vue {
|
|
|
452
565
|
element.classList.add('animate');
|
|
453
566
|
}
|
|
454
567
|
}
|
|
568
|
+
|
|
569
|
+
rebuildPanelOnboardingPopovers(panelId: string | number, refreshDelay?: number) {
|
|
570
|
+
const panelRef = this.$refs[`panel-${panelId}`];
|
|
571
|
+
if (panelRef?.[0]) panelRef[0].rebuildOnboardingPopovers(refreshDelay);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
rebuildAllOnboardingPopovers(refreshDelay?: number) {
|
|
575
|
+
this.panelsStack.forEach((panel) => {
|
|
576
|
+
this.rebuildPanelOnboardingPopovers(panel.id, refreshDelay);
|
|
577
|
+
});
|
|
578
|
+
}
|
|
455
579
|
}
|
|
456
580
|
</script>
|
|
@@ -1,33 +1,61 @@
|
|
|
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
|
|
|
7
|
-
|
|
10
|
+
const COLLAPSE_SYMBOL = '~'
|
|
11
|
+
const PARAMS_SYMBOL = ';'
|
|
12
|
+
|
|
13
|
+
export function stackToHash(stack: IPanel[], routePrefix: string = '') {
|
|
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
|
+
let url = hash;
|
|
20
|
+
if (routePrefix) {
|
|
21
|
+
url = `${routePrefix}/` + url;
|
|
22
|
+
}
|
|
23
|
+
return isPathType() ? `/${url}` : `#${url}`;
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
|
|
15
|
-
export function hashToStack(hash: string|undefined): IPanel[] {
|
|
27
|
+
export function hashToStack(hash: string|undefined, routePrefix: string = ''): IPanel[] {
|
|
16
28
|
let stack:IPanel[] = [];
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
let str = hash.replace(isPathType() ? /^\// : /^#/, '');
|
|
30
|
+
if (routePrefix) {
|
|
31
|
+
const prefix = isPathType() ? `${routePrefix}/` : `${routePrefix}`;
|
|
32
|
+
if (str.startsWith(prefix)) {
|
|
33
|
+
str = str.substring(prefix.length);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (str) {
|
|
37
|
+
stack = str.split(isPathType() ? '/' : '&').map(item => {
|
|
38
|
+
if (!item.includes(PARAMS_SYMBOL)) {
|
|
39
|
+
return { type: item.replace(COLLAPSE_SYMBOL, ''), isCollapsed: item.includes(COLLAPSE_SYMBOL), payload: {} };
|
|
40
|
+
}
|
|
41
|
+
const [type, payload] = item.split(PARAMS_SYMBOL);
|
|
42
|
+
const isCollapsed = type.includes(COLLAPSE_SYMBOL);
|
|
23
43
|
let payloadObj:any = {};
|
|
24
44
|
try {
|
|
25
|
-
|
|
45
|
+
let json = decodeURIComponent(payload);
|
|
46
|
+
if (!json.startsWith('{')) {
|
|
47
|
+
json = `{${json}`; // Ensure it starts with a '{' to be valid JSON
|
|
48
|
+
}
|
|
49
|
+
if (!json.endsWith('}')) {
|
|
50
|
+
json += '}'; // Ensure it ends with a '}' to be valid JSON
|
|
51
|
+
}
|
|
52
|
+
payloadObj = JSON5.parse(json);
|
|
26
53
|
} catch (e) {
|
|
27
54
|
// ignore
|
|
55
|
+
console.warn(`Error parsing payload for type ${type}:`, payload, e);
|
|
28
56
|
}
|
|
29
57
|
return {
|
|
30
|
-
type: type.replace(
|
|
58
|
+
type: type.replace(COLLAPSE_SYMBOL, ''),
|
|
31
59
|
isCollapsed,
|
|
32
60
|
payload: payloadObj
|
|
33
61
|
};
|