@rkosafo/cai.components 0.0.35 → 0.0.36
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.
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { cn } from '../../../utils/index.js';
|
|
3
3
|
import { page } from '$app/state';
|
|
4
|
-
import type
|
|
5
|
-
import
|
|
4
|
+
import { IconifyIcon, type IMenuItem, type TFSidebarProps } from '../../../index.js';
|
|
5
|
+
import { slide } from 'svelte/transition';
|
|
6
6
|
|
|
7
7
|
let {
|
|
8
8
|
homeUrl,
|
|
@@ -16,22 +16,113 @@
|
|
|
16
16
|
}: TFSidebarProps = $props();
|
|
17
17
|
|
|
18
18
|
let activeUrl = $state('');
|
|
19
|
+
let openNestedIndexes = $state<Set<number>>(new Set());
|
|
20
|
+
|
|
19
21
|
$effect(() => {
|
|
20
22
|
activeUrl = page.url.pathname;
|
|
21
23
|
});
|
|
24
|
+
|
|
25
|
+
// Function to toggle nested menu by index
|
|
26
|
+
function toggleNestedMenu(index: number) {
|
|
27
|
+
handleMenuItemClick();
|
|
28
|
+
const newOpenIndexes = new Set(openNestedIndexes);
|
|
29
|
+
|
|
30
|
+
if (newOpenIndexes.has(index)) {
|
|
31
|
+
// If already open, close it
|
|
32
|
+
newOpenIndexes.delete(index);
|
|
33
|
+
} else {
|
|
34
|
+
// If opening new one, just add it (don't clear others)
|
|
35
|
+
newOpenIndexes.add(index);
|
|
36
|
+
}
|
|
37
|
+
openNestedIndexes = newOpenIndexes;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Function to handle regular menu item clicks (non-nested)
|
|
41
|
+
function handleMenuItemClick() {
|
|
42
|
+
// Close all nested menus when clicking a regular menu item
|
|
43
|
+
openNestedIndexes = new Set();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Function to check if a menu item or any of its children is active
|
|
47
|
+
function isItemOrChildActive(item: IMenuItem): boolean {
|
|
48
|
+
if (isActiveFunction) {
|
|
49
|
+
return isActiveFunction(item);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (item.path === activeUrl) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check if any child is active
|
|
57
|
+
if (item.items) {
|
|
58
|
+
return item.items.some((child) => child.path === activeUrl);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
22
63
|
</script>
|
|
23
64
|
|
|
24
65
|
{#snippet menuItemSnippet(item: IMenuItem, active: boolean)}
|
|
25
66
|
<li class:active class="relative">
|
|
26
|
-
<a
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
67
|
+
<a
|
|
68
|
+
class="space-x-3 pl-3 {active ? 'text-[#3C91E6]' : ''}"
|
|
69
|
+
href={item.path}
|
|
70
|
+
onclick={() => handleMenuItemClick()}
|
|
71
|
+
>
|
|
72
|
+
<IconifyIcon icon={item.icon} class={active ? 'text-[#3C91E6]' : ''} />
|
|
73
|
+
<span class="text hidden md:block {active ? 'text-[#3C91E6]' : ''}">{item.title}</span>
|
|
30
74
|
</a>
|
|
31
75
|
</li>
|
|
32
76
|
{/snippet}
|
|
77
|
+
|
|
78
|
+
{#snippet nestedItemSnippet(item: IMenuItem, index: number)}
|
|
79
|
+
{@const isActive = isItemOrChildActive(item)}
|
|
80
|
+
{@const isOpen = openNestedIndexes.has(index)}
|
|
81
|
+
|
|
82
|
+
<div class="mx-1 rounded-[5px] px-1.5 py-2 {isActive ? 'bg-[#cfe8ff79] text-[#3C91E6]' : ''}">
|
|
83
|
+
<button
|
|
84
|
+
class="flex w-full items-center space-x-3 pl-3"
|
|
85
|
+
onclick={() => toggleNestedMenu(index)}
|
|
86
|
+
type="button"
|
|
87
|
+
>
|
|
88
|
+
<IconifyIcon icon={item.icon} class={isActive ? 'text-[#3C91E6]' : ''} />
|
|
89
|
+
<span class="text hidden md:block {isActive ? 'text-[#3C91E6]' : ''}">{item.title}</span>
|
|
90
|
+
<IconifyIcon
|
|
91
|
+
icon="ri:arrow-down-s-line"
|
|
92
|
+
class="ml-auto transition-transform duration-200 {isOpen ? 'rotate-180' : ''}"
|
|
93
|
+
/>
|
|
94
|
+
</button>
|
|
95
|
+
{#if isOpen}
|
|
96
|
+
<ul transition:slide class="sub-menu space-y-0 py-1">
|
|
97
|
+
{#each item.items! as child}
|
|
98
|
+
{@const isChildActive = isActiveFunction
|
|
99
|
+
? isActiveFunction(child)
|
|
100
|
+
: activeUrl === child.path}
|
|
101
|
+
<div class="pl-4">
|
|
102
|
+
<li class="relative">
|
|
103
|
+
<a
|
|
104
|
+
class="flex items-center space-x-3 rounded transition-colors hover:bg-[#cfe8ff] {isChildActive
|
|
105
|
+
? 'bg-[#cfe8ff] text-[#3C91E6]'
|
|
106
|
+
: ''}"
|
|
107
|
+
href={child.path}
|
|
108
|
+
>
|
|
109
|
+
{#if child.icon}
|
|
110
|
+
<IconifyIcon icon={child.icon} class={isChildActive ? 'text-[#3C91E6]' : ''} />
|
|
111
|
+
{/if}
|
|
112
|
+
<span class="text hidden md:block {isChildActive ? 'text-[#3C91E6]' : ''}"
|
|
113
|
+
>{child.title}</span
|
|
114
|
+
>
|
|
115
|
+
</a>
|
|
116
|
+
</li>
|
|
117
|
+
</div>
|
|
118
|
+
{/each}
|
|
119
|
+
</ul>
|
|
120
|
+
{/if}
|
|
121
|
+
</div>
|
|
122
|
+
{/snippet}
|
|
123
|
+
|
|
33
124
|
<section id="tf-sidebar" class={cn('relative')} class:hide={hideSidebar}>
|
|
34
|
-
<a href={homeUrl} class="brand flex flex-col pt-4">
|
|
125
|
+
<a href={homeUrl} class="brand flex flex-col pt-4" onclick={() => handleMenuItemClick()}>
|
|
35
126
|
{#if logo}
|
|
36
127
|
{@render logo()}
|
|
37
128
|
{:else}
|
|
@@ -44,9 +135,13 @@
|
|
|
44
135
|
</a>
|
|
45
136
|
{#if menuItems.length > 0}
|
|
46
137
|
<ul class="side-menu top relative pt-4">
|
|
47
|
-
{#each menuItems as item}
|
|
138
|
+
{#each menuItems as item, index}
|
|
48
139
|
{@const active = isActiveFunction ? isActiveFunction(item) : activeUrl === item.path}
|
|
49
|
-
{
|
|
140
|
+
{#if item.items}
|
|
141
|
+
{@render nestedItemSnippet(item, index)}
|
|
142
|
+
{:else}
|
|
143
|
+
{@render menuItemSnippet(item, active)}
|
|
144
|
+
{/if}
|
|
50
145
|
{/each}
|
|
51
146
|
</ul>
|
|
52
147
|
{/if}
|