Skip to content

Commit d687449

Browse files
committed
feat: add isSameTab allowing user to compare two tabs by their data
1 parent 42d7442 commit d687449

File tree

7 files changed

+296
-80
lines changed

7 files changed

+296
-80
lines changed

src/App.vue

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,25 @@ import { Direction } from "./lib/types";
33
import useLayout, { createLayout, createTab } from "./lib/useLayout";
44
import VLayout from "./lib/VLayout.vue";
55
6-
const options = useLayout(
6+
const { options } = useLayout(
77
createLayout({
88
direction: Direction.Column,
99
children: [
1010
createLayout({
11-
children: [
12-
createTab({ title: "1", data: { id: 1 } }),
13-
createTab({ title: "2", data: { id: 2 } }),
14-
],
11+
children: [createTab({ title: "Hello" })],
1512
}),
1613
createLayout({
17-
children: [
18-
createTab({ title: "3", data: { id: 3 } }),
19-
createTab({ title: "4", data: { id: 4 } }),
20-
],
14+
children: [createTab({ title: "2" }), createTab({ title: "3" })],
2115
}),
2216
],
2317
}),
2418
{
25-
onUnknownDropped(data) {
19+
onUnknownDropped(data: Record<string, unknown>) {
2620
return createTab({ title: "Data", data });
2721
},
22+
areSameTab() {
23+
return false;
24+
},
2825
}
2926
);
3027
</script>
@@ -33,9 +30,7 @@ const options = useLayout(
3330
<VLayout :options="options">
3431
<template #tab="props">
3532
<div>
36-
{{ props.title }} => Lorem ipsum dolor sit amet, consectetur adipisicing elit. Perspiciatis
37-
quis eveniet consequuntur vel! Ducimus, eligendi? Et laboriosam reiciendis magni quidem cum,
38-
iste veritatis nam vero accusantium? Odit, facilis. Ipsam, at.
33+
<h3>{{ props.id }}</h3>
3934
</div>
4035
</template>
4136
</VLayout>

src/lib/VLayout.vue

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
<script lang="ts" setup>
22
import { computed } from "vue";
33
import { Layout, Tab, UIType, Direction, Side, UseLayoutOutput } from "./types";
4-
import { getType, getTab, useOnDrop, useCloseTab, useToggleTab } from "./useLayout";
4+
import { getType, getTab } from "./useLayout";
55
import VTabButton from "./VTabButton.vue";
66
import VTabContent from "./VTabContent.vue";
77
88
const { options } = defineProps<{
99
options: UseLayoutOutput;
1010
}>();
1111
12+
if ((options.tree.type as any) === UIType.Tab) {
13+
console.log(options.tree);
14+
}
15+
1216
const forLayouts = computed(() => getType(options.tree.children) === UIType.Layout);
1317
1418
const isEmpty = computed(() => options.tree.children.length === 0);
@@ -23,7 +27,7 @@ const classList = computed(() => {
2327
list.push("clv__layout-container-col");
2428
}
2529
26-
return list.toString();
30+
return list.join(" ");
2731
});
2832
2933
const toggle = (id: string) => {
@@ -68,7 +72,7 @@ const dropped = ({ side, ev }: { ev: DragEvent; side: Side }) => {
6872
></slot>
6973
</VTabContent>
7074
</div>
71-
<div v-else class="clv__layouts-container" :class="classList">
75+
<div v-else-if="forLayouts" class="clv__layouts-container" :class="classList">
7276
<VLayout
7377
v-for="item in (options.tree.children as Array<Layout>)"
7478
:key="item.id"

src/lib/VTabButton.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const onDragStart = (e: DragEvent) => {
1717
const data: DraggedTab = {
1818
id: item.id,
1919
type: UIType.Tab,
20-
title: item.title,
2120
data: item.data,
2221
signature: "__dragged__tab__",
2322
};

src/lib/tests/useLayout.test.ts

Lines changed: 198 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ describe("useLayout", () => {
3535

3636
describe("createLayout", () => {
3737
it("should create a layout template with minimal options", () => {
38-
expect(createLayout({ children: [] })).toStrictEqual({ children: [], type: UIType.Layout });
38+
expect(createLayout({ children: [] })).toStrictEqual({
39+
children: [],
40+
type: UIType.Layout,
41+
direction: undefined,
42+
});
3943
});
4044

4145
it("should create a layout template with minimal options", () => {
@@ -748,7 +752,7 @@ describe("useLayout", () => {
748752
})
749753
);
750754

751-
useAddTab(createTab({ title: "5", data: { id: 5 } }), layout, 1);
755+
useAddTab(createTab({ title: "5", data: { id: 5 } }), layout, () => false, 1);
752756

753757
expect(layout.children.length).toBe(4);
754758
expect(layout.children[0].title).toBe("1");
@@ -757,6 +761,25 @@ describe("useLayout", () => {
757761
expect(layout.children[2].title).toBe("2");
758762
expect(layout.children[3].title).toBe("3");
759763
});
764+
765+
it("should not add tab", () => {
766+
const layout = transformLayoutTemplate(
767+
createLayout({
768+
children: [
769+
createTab({ title: "1" }),
770+
createTab({ title: "2" }),
771+
createTab({ title: "3" }),
772+
],
773+
})
774+
);
775+
776+
useAddTab(createTab({ title: "5", data: { id: 5 } }), layout, () => true, 1);
777+
778+
expect(layout.children.length).toBe(3);
779+
expect(layout.children[0].title).toBe("1");
780+
expect(layout.children[1].title).toBe("2");
781+
expect(layout.children[2].title).toBe("3");
782+
});
760783
});
761784

762785
describe("useOnDrop", () => {
@@ -765,9 +788,15 @@ describe("useLayout", () => {
765788
createLayout({ children: [createTab({ title: "1", data: { id: 1 } })] })
766789
);
767790

768-
useOnDrop({ id: 2 }, layout, Side.Center, (data) => {
769-
return createTab({ title: "2", data });
770-
});
791+
useOnDrop(
792+
{ id: 2 },
793+
layout,
794+
Side.Center,
795+
() => false,
796+
(data) => {
797+
return createTab({ title: "2", data });
798+
}
799+
);
771800

772801
expect(layout.children.length).toBe(2);
773802
expect(layout.children[0].data).toStrictEqual({ id: 1 });
@@ -802,7 +831,13 @@ describe("useLayout", () => {
802831
id: parent.children[1].children[0].id,
803832
};
804833

805-
useOnDrop(data, layout, Side.Center, () => undefined);
834+
useOnDrop(
835+
data,
836+
layout,
837+
Side.Center,
838+
() => false,
839+
() => undefined
840+
);
806841

807842
expect(layout.children.length).toBe(3);
808843
expect(layout.children[0].data).toStrictEqual({ id: 1 });
@@ -829,9 +864,15 @@ describe("useLayout", () => {
829864
createLayout({ children: [createTab({ title: "1", data: { id: 1 } })] })
830865
);
831866

832-
useOnDrop({ id: 2 }, layout, side, (data) => {
833-
return createTab({ title: "2", data });
834-
});
867+
useOnDrop(
868+
{ id: 2 },
869+
layout,
870+
side,
871+
() => false,
872+
(data) => {
873+
return createTab({ title: "2", data });
874+
}
875+
);
835876

836877
expect(layout.children.length).toBe(2);
837878
expect(getType(layout.children)).toBe(UIType.Layout);
@@ -854,6 +895,24 @@ describe("useLayout", () => {
854895
}
855896
);
856897

898+
it("should not add tab to the center", () => {
899+
const layout = transformLayoutTemplate(
900+
createLayout({ children: [createTab({ title: "1", data: { id: 1 } })] })
901+
);
902+
903+
useOnDrop(
904+
{ id: 2 },
905+
layout,
906+
Side.Center,
907+
() => true,
908+
(data) => {
909+
return createTab({ title: "2", data });
910+
}
911+
);
912+
913+
expect(layout.children.length).toBe(1);
914+
});
915+
857916
describe("Parent with same direction as drop", () => {
858917
let parent: Layout<Layout>;
859918

@@ -881,9 +940,15 @@ describe("useLayout", () => {
881940

882941
const layout = parent.children[1];
883942

884-
useOnDrop({ id: "test" }, layout, side, (data) => {
885-
return createTab({ title: "test", data });
886-
});
943+
useOnDrop(
944+
{ id: "test" },
945+
layout,
946+
side,
947+
() => false,
948+
(data) => {
949+
return createTab({ title: "test", data });
950+
}
951+
);
887952

888953
expect(parent.children.length).toBe(4);
889954

@@ -938,9 +1003,15 @@ describe("useLayout", () => {
9381003
parent.direction = direction;
9391004
const layout = parent.children[0];
9401005

941-
useOnDrop({ id: "test" }, layout, side, (data) => {
942-
return createTab({ title: "test", data });
943-
});
1006+
useOnDrop(
1007+
{ id: "test" },
1008+
layout,
1009+
side,
1010+
() => false,
1011+
(data) => {
1012+
return createTab({ title: "test", data });
1013+
}
1014+
);
9441015

9451016
expect(parent.children.length).toBe(3);
9461017

@@ -979,5 +1050,117 @@ describe("useLayout", () => {
9791050
expect(l3.children[0].title).toBe("3");
9801051
});
9811052
});
1053+
1054+
it("should move tab from Layout<Tab> to the [Right] of same level Layout<Tab>", () => {
1055+
const parent = transformLayoutTemplate(
1056+
createLayout({
1057+
direction: Direction.Column,
1058+
children: [
1059+
createLayout({
1060+
children: [createTab({ title: "1", data: { id: 1 } })],
1061+
}),
1062+
createLayout({
1063+
children: [createTab({ title: "2", data: { id: 2 } })],
1064+
}),
1065+
],
1066+
})
1067+
) as unknown as Layout<Layout>;
1068+
1069+
const layout = parent.children[0];
1070+
1071+
const id = parent.children[1].children[0].id;
1072+
1073+
const dragData: DraggedTab = {
1074+
id,
1075+
signature: "__dragged__tab__",
1076+
type: UIType.Tab,
1077+
data: parent.children[1].children[0].data,
1078+
};
1079+
1080+
useOnDrop(
1081+
dragData,
1082+
layout,
1083+
Side.Right,
1084+
() => false,
1085+
() => undefined
1086+
);
1087+
1088+
expect(parent.direction).toBe(Direction.Row);
1089+
expect(parent.type).toBe(UIType.Layout);
1090+
1091+
expect(parent.children.length).toBe(2);
1092+
1093+
const c1 = parent.children[0];
1094+
expect(c1.parent === parent).toBe(true);
1095+
expect(c1.children.length).toBe(1);
1096+
expect(c1.children[0].title).toBe("1");
1097+
expect(c1.children[0].data).toStrictEqual({ id: 1 });
1098+
1099+
const c2 = parent.children[1];
1100+
expect(c2.parent === parent).toBe(true);
1101+
expect(c2.children.length).toBe(1);
1102+
expect(c2.children[0].title).toBe("2");
1103+
expect(c2.children[0].data).toStrictEqual({ id: 2 });
1104+
});
1105+
1106+
it("should move tab from Layout<Tab> to the [Right] of same level Layout<Tab>", () => {
1107+
const parent = transformLayoutTemplate(
1108+
createLayout({
1109+
direction: Direction.Column,
1110+
children: [
1111+
createLayout({
1112+
children: [createTab({ title: "1", data: { id: 1 } })],
1113+
}),
1114+
createLayout({
1115+
children: [createTab({ title: "2", data: { id: 2 } })],
1116+
}),
1117+
createLayout({
1118+
children: [createTab({ title: "3", data: { id: 3 } })],
1119+
}),
1120+
],
1121+
})
1122+
) as unknown as Layout<Layout>;
1123+
1124+
const layout = parent.children[0];
1125+
1126+
const id = parent.children[1].children[0].id;
1127+
1128+
const dragData: DraggedTab = {
1129+
id,
1130+
signature: "__dragged__tab__",
1131+
type: UIType.Tab,
1132+
data: parent.children[1].children[0].data,
1133+
};
1134+
1135+
useOnDrop(
1136+
dragData,
1137+
layout,
1138+
Side.Right,
1139+
() => false,
1140+
() => undefined
1141+
);
1142+
1143+
expect(parent.direction).toBe(Direction.Column);
1144+
expect(parent.type).toBe(UIType.Layout);
1145+
1146+
expect(parent.children.length).toBe(2);
1147+
1148+
const c1 = parent.children[0] as unknown as Layout<Layout>;
1149+
expect(c1.parent === parent).toBe(true);
1150+
expect(getType(c1.children)).toBe(UIType.Layout);
1151+
expect(c1.children.length).toBe(2);
1152+
expect(c1.active).toBeUndefined;
1153+
expect(c1.children[0].children[0].title).toBe("1");
1154+
expect(c1.children[0].children[0].data).toStrictEqual({ id: 1 });
1155+
expect(c1.children[1].children[0].title).toBe("2");
1156+
expect(c1.children[1].children[0].data).toStrictEqual({ id: 2 });
1157+
1158+
const c2 = parent.children[1];
1159+
expect(c2.parent === parent).toBe(true);
1160+
expect(c2.children.length).toBe(1);
1161+
expect(c2.type).toBe(UIType.Layout);
1162+
expect(c2.children[0].title).toBe("3");
1163+
expect(c2.children[0].data).toStrictEqual({ id: 3 });
1164+
});
9821165
});
9831166
});

src/lib/types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,12 @@ export interface LayoutActions {
5252
toggleTab: (id: string) => void;
5353
closeTab: (id: string) => void;
5454
addTab: (tab: TabTemplate, LayoutId: string, position?: number) => void;
55-
onDrop: (data: Record<string, unknown>, layoutId: string, side: Side) => void;
55+
onDrop: (
56+
data: Record<string, unknown>,
57+
layoutId: string,
58+
side: Side,
59+
isSame?: (t1: any, t2: any) => boolean
60+
) => void;
5661
}
5762

5863
export type DraggedSignature = "__dragged__tab__";
@@ -62,8 +67,9 @@ export interface DraggedTab extends Omit<TabTemplate<Record<string, unknown>>, "
6267
signature: DraggedSignature;
6368
}
6469

65-
export interface UseLayoutOptions {
70+
export interface UseLayoutOptions<T> {
6671
onUnknownDropped: (data: Record<string, unknown>) => TabTemplate | undefined;
72+
areSameTab?: (tab1Data: T, tab2Data: T) => boolean;
6773
}
6874

6975
export interface UseLayoutOutput {

0 commit comments

Comments
 (0)