@itfin/components 2.0.30 → 2.0.31
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 +16 -20
- package/src/components/icon/collapse.svg +6 -0
- package/src/components/icon/components/nomi-project.vue +2 -2
- package/src/components/icon/icons.js +307 -307
- package/src/components/panels/Panel.vue +14 -32
- package/src/components/panels/PanelLink.vue +5 -5
- package/src/components/panels/PanelList.vue +41 -14
- package/src/components/panels/helpers.ts +10 -9
- package/src/components/panels/index.ts +24 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div tabindex="-1" :class="{'b-panel': !nocard, 'b-panel__collapsed': collapsed, 'b-panel__active': !collapsed}">
|
|
3
|
-
<
|
|
4
|
-
<itf-icon name="bell" :size="24" new class="b-panel-alert__icon" />
|
|
5
|
-
<p class="mb-0">{{ alert }}</p>
|
|
6
|
-
</div>
|
|
3
|
+
<slot name="before-header"></slot>
|
|
7
4
|
<div v-if="collapsed && !nocard" class="b-panel__expand" @click.stop.prevent="expandPanel">
|
|
8
5
|
<itf-button v-if="closeable" icon small class="b-panel__expand_button" @click="closePanel">
|
|
9
6
|
<itf-icon name="cross" />
|
|
@@ -26,9 +23,14 @@
|
|
|
26
23
|
</div>
|
|
27
24
|
<div class="d-flex gap-1">
|
|
28
25
|
<slot name="buttons"></slot>
|
|
29
|
-
<
|
|
30
|
-
<itf-icon
|
|
31
|
-
|
|
26
|
+
<template v-if="expandable">
|
|
27
|
+
<itf-button v-if="!isFullSize" icon default class="b-panel__expand_button d-none d-md-block" @click="fullsizePanel">
|
|
28
|
+
<itf-icon new name="expand" />
|
|
29
|
+
</itf-button>
|
|
30
|
+
<itf-button v-else icon default class="b-panel__expand_button d-none d-md-block" @click="collapsePanel">
|
|
31
|
+
<itf-icon new name="collapse" />
|
|
32
|
+
</itf-button>
|
|
33
|
+
</template>
|
|
32
34
|
<itf-button v-if="closeable" icon default class="b-panel__expand_button" @click="closePanel">
|
|
33
35
|
<itf-icon new name="close" />
|
|
34
36
|
</itf-button>
|
|
@@ -161,29 +163,6 @@
|
|
|
161
163
|
top: 0;
|
|
162
164
|
z-index: 10;
|
|
163
165
|
}
|
|
164
|
-
.b-panel-alert {
|
|
165
|
-
border-radius: 6px 6px 0 0;
|
|
166
|
-
padding: 2px 16px;
|
|
167
|
-
font-size: 12px;
|
|
168
|
-
line-height: 16px;
|
|
169
|
-
text-align: center;
|
|
170
|
-
color: var(--b-panel-white);
|
|
171
|
-
background-color: var(--b-panel-info);
|
|
172
|
-
|
|
173
|
-
&__icon {
|
|
174
|
-
animation: bellRing 5s ease-in-out infinite;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
&--success {
|
|
178
|
-
background-color: var(--b-panel-success);
|
|
179
|
-
}
|
|
180
|
-
&--warning {
|
|
181
|
-
background-color: var(--b-panel-warning);
|
|
182
|
-
}
|
|
183
|
-
&--danger {
|
|
184
|
-
background-color: var(--b-panel-danger);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
166
|
</style>
|
|
188
167
|
<script>
|
|
189
168
|
import { Vue, Prop, Component } from 'vue-property-decorator';
|
|
@@ -211,11 +190,10 @@ class Panel extends Vue {
|
|
|
211
190
|
@Prop() panel;
|
|
212
191
|
@Prop(Boolean) collapsed;
|
|
213
192
|
@Prop(Boolean) closeable;
|
|
193
|
+
@Prop(Boolean) isFullSize;
|
|
214
194
|
@Prop(Boolean) expandable;
|
|
215
195
|
@Prop(Boolean) animate;
|
|
216
196
|
@Prop(Boolean) nocard;
|
|
217
|
-
@Prop(String) alert;
|
|
218
|
-
@Prop({ type: String, default: 'info' }) alertType; // info | success | warning | danger
|
|
219
197
|
|
|
220
198
|
openPanel(...args) {
|
|
221
199
|
this.$emit('open', args);
|
|
@@ -225,6 +203,10 @@ class Panel extends Vue {
|
|
|
225
203
|
this.$emit('expand');
|
|
226
204
|
}
|
|
227
205
|
|
|
206
|
+
collapsePanel() {
|
|
207
|
+
this.$emit('collapse');
|
|
208
|
+
}
|
|
209
|
+
|
|
228
210
|
fullsizePanel() {
|
|
229
211
|
this.$emit('fullsize');
|
|
230
212
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { Vue, Component, Inject, Prop } from 'vue-property-decorator';
|
|
6
6
|
import { IPanel } from './PanelList.vue';
|
|
7
7
|
import {stackToHash} from "@itfin/components/src/components/panels/helpers";
|
|
8
|
+
import {getRootPanelList} from "@itfin/components/src/components/panels";
|
|
8
9
|
|
|
9
10
|
@Component({
|
|
10
11
|
components: {
|
|
@@ -15,7 +16,6 @@ import {stackToHash} from "@itfin/components/src/components/panels/helpers";
|
|
|
15
16
|
}
|
|
16
17
|
})
|
|
17
18
|
export default class PanelLink extends Vue {
|
|
18
|
-
@Inject({ default: null }) panelList;
|
|
19
19
|
@Inject({ default: null }) currentPanel;
|
|
20
20
|
|
|
21
21
|
@Prop(Boolean) global: boolean;
|
|
@@ -35,7 +35,7 @@ export default class PanelLink extends Vue {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
get activeList() {
|
|
38
|
-
return this.list ??
|
|
38
|
+
return this.list ?? getRootPanelList();
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
get isActive() {
|
|
@@ -57,11 +57,13 @@ export default class PanelLink extends Vue {
|
|
|
57
57
|
type: this.panel,
|
|
58
58
|
payload: this.payload || {}
|
|
59
59
|
}
|
|
60
|
-
]
|
|
60
|
+
]);
|
|
61
61
|
return hash;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
onClick(e) {
|
|
65
|
+
e.preventDefault();
|
|
66
|
+
e.stopPropagation();
|
|
65
67
|
this.$emit('open', {
|
|
66
68
|
panel: this.panel,
|
|
67
69
|
payload: this.payload || {},
|
|
@@ -70,8 +72,6 @@ export default class PanelLink extends Vue {
|
|
|
70
72
|
if (!this.activeList) {
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
73
|
-
e.preventDefault();
|
|
74
|
-
e.stopPropagation();
|
|
75
75
|
this.activeList.openPanel(this.panel, this.payload || {}, this.append ? undefined : this.currentPanel?.index + 1);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -17,17 +17,20 @@
|
|
|
17
17
|
:icon="panel.icon"
|
|
18
18
|
:payload="panel.payload"
|
|
19
19
|
:expandable="panelsStack.length > 1"
|
|
20
|
+
:isFullSize="isFullSize"
|
|
20
21
|
:collapsed="panel.isCollapsed"
|
|
21
22
|
:closeable="panel.isCloseable"
|
|
22
23
|
:animate="panel.isAnimate"
|
|
23
|
-
:alert="panel.alert"
|
|
24
|
-
:alertType="panel.alertType"
|
|
25
24
|
@open="openPanel($event[0], $event[1], n + 1)"
|
|
26
25
|
@expand="expandPanel(panel)"
|
|
27
26
|
@fullsize="fullsizePanel(panel)"
|
|
27
|
+
@collapse="collapsePanel(panel)"
|
|
28
28
|
@close="closePanel(panel)"
|
|
29
29
|
@open-menu="$emit('open-menu', panel.type, panel.payload)"
|
|
30
30
|
>
|
|
31
|
+
<template #before-header>
|
|
32
|
+
<slot name="before-header" :panel="panel" :index="n" :payload="panel.payload"></slot>
|
|
33
|
+
</template>
|
|
31
34
|
<slot
|
|
32
35
|
:name="panel.type"
|
|
33
36
|
:panel="panel"
|
|
@@ -151,7 +154,6 @@ $double-an-time: $an-time * 2;
|
|
|
151
154
|
//transition: opacity $an-time linear;
|
|
152
155
|
}
|
|
153
156
|
}
|
|
154
|
-
|
|
155
157
|
//.slide-enter-active > div {
|
|
156
158
|
// opacity: 0;
|
|
157
159
|
//}
|
|
@@ -162,16 +164,16 @@ $double-an-time: $an-time * 2;
|
|
|
162
164
|
</style>
|
|
163
165
|
<script lang="ts">
|
|
164
166
|
import { Vue, Component, Prop } from 'vue-property-decorator';
|
|
167
|
+
import itfIcon from '../icon/Icon.vue';
|
|
165
168
|
import Panel from './Panel.vue';
|
|
166
169
|
import {hashToStack, stackToHash} from "@itfin/components/src/components/panels/helpers";
|
|
170
|
+
import {setRootPanelList} from "@itfin/components/src/components/panels";
|
|
167
171
|
|
|
168
172
|
interface VisualOptions {
|
|
169
173
|
title: string;
|
|
170
174
|
icon?: string;
|
|
171
175
|
}
|
|
172
176
|
|
|
173
|
-
export type PanelAlertType = 'info' | 'success' | 'warning' | 'danger';
|
|
174
|
-
|
|
175
177
|
export interface IPanel {
|
|
176
178
|
id: number;
|
|
177
179
|
title: string;
|
|
@@ -182,8 +184,6 @@ export interface IPanel {
|
|
|
182
184
|
isCollapsed: boolean;
|
|
183
185
|
isCloseable: boolean;
|
|
184
186
|
isAnimate: boolean;
|
|
185
|
-
alert?: string;
|
|
186
|
-
alertType?: PanelAlertType;
|
|
187
187
|
open: (type: string, visOptions: VisualOptions, payload: any) => void;
|
|
188
188
|
close: () => void;
|
|
189
189
|
expand: () => void;
|
|
@@ -202,6 +202,7 @@ export interface IPanel {
|
|
|
202
202
|
|
|
203
203
|
@Component({
|
|
204
204
|
components: {
|
|
205
|
+
itfIcon,
|
|
205
206
|
Panel
|
|
206
207
|
},
|
|
207
208
|
directives: {
|
|
@@ -223,6 +224,7 @@ export default class PanelList extends Vue {
|
|
|
223
224
|
nextId:number = 0;
|
|
224
225
|
|
|
225
226
|
created() {
|
|
227
|
+
setRootPanelList(this);
|
|
226
228
|
if (this.firstPanel) {
|
|
227
229
|
this.internalOpenPanel(this.firstPanel.type, this.firstPanel.payload);
|
|
228
230
|
}
|
|
@@ -289,15 +291,10 @@ export default class PanelList extends Vue {
|
|
|
289
291
|
if (typeof panel.caption !== 'function') {
|
|
290
292
|
throw new Error('Panel component must have a "caption" function');
|
|
291
293
|
}
|
|
292
|
-
if (this.panels[type].alert && typeof this.panels[type].alert !== 'function') {
|
|
293
|
-
throw new Error('Panel component "alert" field must have function type');
|
|
294
|
-
}
|
|
295
294
|
const newPanel:any = {
|
|
296
295
|
id: this.nextId++,
|
|
297
296
|
nocard: panel.nocard,
|
|
298
297
|
title: panel.caption(this.$t.bind(this), payload),
|
|
299
|
-
alert: this.panels[type].alert ? this.panels[type].alert(this.$t.bind(this), payload) : undefined,
|
|
300
|
-
alertType: this.panels[type].alertType,
|
|
301
298
|
icon: panel.icon ? panel.icon(this.$t.bind(this), payload) : null,
|
|
302
299
|
components: {
|
|
303
300
|
default: panel.default ?? undefined,
|
|
@@ -325,6 +322,10 @@ export default class PanelList extends Vue {
|
|
|
325
322
|
isAnimation = newStack.length === openIndex;
|
|
326
323
|
newStack = newStack.slice(0, openIndex);
|
|
327
324
|
}
|
|
325
|
+
if (newStack.length > 0 && !newStack.find(p => !p.isCollapsed)) {
|
|
326
|
+
// якщо немає відкритих панелей, то перша панель має бути розгорнута
|
|
327
|
+
newStack[0].isCollapsed = false;
|
|
328
|
+
}
|
|
328
329
|
this.panelsStack = newStack;
|
|
329
330
|
return new Promise(res => {
|
|
330
331
|
this.$nextTick(() => { // щоб панелі змінювались при редагуванні
|
|
@@ -364,8 +365,6 @@ export default class PanelList extends Vue {
|
|
|
364
365
|
newPanel.setPayload = (value: any) => {
|
|
365
366
|
newPanel.payload = value;
|
|
366
367
|
newPanel.title = panel.caption(this.$t.bind(this), value);
|
|
367
|
-
newPanel.alert = panel.alert ? panel.alert(this.$t.bind(this), payload) : undefined;
|
|
368
|
-
newPanel.alertType = panel.alertType;
|
|
369
368
|
newPanel.icon = panel.icon ? panel.icon(this.$t.bind(this), payload) : null,
|
|
370
369
|
this.setPanelHash()
|
|
371
370
|
}
|
|
@@ -416,12 +415,40 @@ export default class PanelList extends Vue {
|
|
|
416
415
|
fullsizePanel(panel: IPanel) {
|
|
417
416
|
const newStack = [...this.panelsStack];
|
|
418
417
|
for (const p of newStack) {
|
|
418
|
+
p.isLastOpened = !p.isCollapsed && p !== panel;
|
|
419
419
|
p.isCollapsed = p !== panel;
|
|
420
420
|
}
|
|
421
421
|
this.panelsStack = newStack;
|
|
422
422
|
this.setPanelHash();
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
get isFullSize() {
|
|
426
|
+
return this.panelsStack.filter(p => !p.isCollapsed).length === 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
expandPanel(panel: IPanel) {
|
|
430
|
+
const newStack = [...this.panelsStack];
|
|
431
|
+
const index = newStack.findIndex(p => p.id === panel.id);
|
|
432
|
+
newStack[index].isCollapsed = false;
|
|
433
|
+
this.panelsStack = newStack;
|
|
434
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
435
|
+
this.setPanelHash();
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
collapsePanel(panel: IPanel) {
|
|
439
|
+
const newStack = [...this.panelsStack];
|
|
440
|
+
const currenctIndex = newStack.findIndex(p => p.id === panel.id);
|
|
441
|
+
const lastOpenedIndex = newStack.findIndex(p => p.isLastOpened);
|
|
442
|
+
if (lastOpenedIndex !== -1) { // якщо зебрежена остання відкрита панель
|
|
443
|
+
newStack[lastOpenedIndex].isCollapsed = false
|
|
444
|
+
} else if (newStack[currenctIndex-1]) { // якщо після оновлення сторінки відсутнє значення "остання відкрита", то відкриваємо ту, що зліва
|
|
445
|
+
newStack[currenctIndex-1].isCollapsed = false;
|
|
446
|
+
}
|
|
447
|
+
this.panelsStack = newStack;
|
|
448
|
+
this.ensureOnlyTwoOpenPanels(panel.id);
|
|
449
|
+
this.setPanelHash();
|
|
450
|
+
}
|
|
451
|
+
|
|
425
452
|
getPanels(type) {
|
|
426
453
|
return this.panelsStack.filter(panel => panel.type === type);
|
|
427
454
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import JSON5 from 'json5'
|
|
2
|
+
import {isPathType} from "@itfin/components/src/components/panels";
|
|
2
3
|
|
|
3
4
|
export interface IPanel {
|
|
4
5
|
type: string;
|
|
@@ -6,27 +7,27 @@ export interface IPanel {
|
|
|
6
7
|
isCollapsed?: boolean;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export function stackToHash(stack: IPanel[]
|
|
10
|
+
export function stackToHash(stack: IPanel[]) {
|
|
10
11
|
const hash = stack.map(panel => {
|
|
11
12
|
let json = JSON5.stringify(panel.payload || {});
|
|
12
13
|
json = json.substring(1, json.length - 1); // Remove the outer {}
|
|
13
|
-
return `${panel.type}${panel.isCollapsed ? '' : '
|
|
14
|
-
}).join(
|
|
15
|
-
return
|
|
14
|
+
return `${panel.type}${panel.isCollapsed ? '!' : ''}${json ? '=' : ''}${json}`;
|
|
15
|
+
}).join(isPathType() ? '/' : '&');
|
|
16
|
+
return isPathType() ? `/${hash}` : `#${hash}`;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
export function hashToStack(hash: string|undefined
|
|
20
|
+
export function hashToStack(hash: string|undefined): IPanel[] {
|
|
20
21
|
let stack:IPanel[] = [];
|
|
21
22
|
if (hash) {
|
|
22
|
-
const str = hash.replace(
|
|
23
|
+
const str = hash.replace(isPathType() ? /^\// : /^#/, '');
|
|
23
24
|
|
|
24
|
-
stack = str.split(
|
|
25
|
+
stack = str.split(isPathType() ? '/' : '&').map(item => {
|
|
25
26
|
if (!item.includes('=')) {
|
|
26
|
-
return { type: item.replace('!', ''), isCollapsed:
|
|
27
|
+
return { type: item.replace('!', ''), isCollapsed: item.includes('!'), payload: {} };
|
|
27
28
|
}
|
|
28
29
|
const [type, payload] = item.split('=');
|
|
29
|
-
const isCollapsed =
|
|
30
|
+
const isCollapsed = type.includes('!');
|
|
30
31
|
let payloadObj:any = {};
|
|
31
32
|
try {
|
|
32
33
|
let json = decodeURIComponent(payload);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const PanelsSettings = {
|
|
2
|
+
pathType: 'path', // 'hash' | 'path'
|
|
3
|
+
rootPanelList: null, // This will be set to the root panel list when the app is initialized
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export function getPanelsSettings() {
|
|
7
|
+
return PanelsSettings;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getRootPanelList() {
|
|
11
|
+
return PanelsSettings.rootPanelList;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isPathType() {
|
|
15
|
+
return PanelsSettings.pathType === 'path';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function setPanelsPathType(settings: any) {
|
|
19
|
+
PanelsSettings.pathType = settings.pathType ?? 'path';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function setRootPanelList(rootPanelList: any) {
|
|
23
|
+
PanelsSettings.rootPanelList = rootPanelList;
|
|
24
|
+
}
|