@rkosafo/cai.components 0.0.3 → 0.0.6

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.
Files changed (61) hide show
  1. package/README.md +8 -8
  2. package/dist/baseEditor/index.svelte +32 -32
  3. package/dist/builders/filters/FilterBuilder.svelte +638 -638
  4. package/dist/forms/FormCheckbox/FormCheckbox.svelte +53 -53
  5. package/dist/forms/FormDatepicker/FormDatepicker.svelte +159 -159
  6. package/dist/forms/FormInput/FormInput.svelte +87 -87
  7. package/dist/forms/FormRadio/FormRadio.svelte +53 -53
  8. package/dist/forms/FormSelect/FormSelect.svelte +86 -86
  9. package/dist/forms/FormTextarea/FormTextarea.svelte +77 -77
  10. package/dist/forms/checkbox/Checkbox.svelte +82 -82
  11. package/dist/forms/checkbox/CheckboxButton.svelte +92 -92
  12. package/dist/forms/datepicker/Datepicker.svelte +706 -706
  13. package/dist/forms/form/Form.svelte +69 -69
  14. package/dist/forms/input/Input.svelte +363 -363
  15. package/dist/forms/label/Label.svelte +38 -38
  16. package/dist/forms/radio/Radio.svelte +48 -48
  17. package/dist/forms/radio/RadioButton.svelte +22 -22
  18. package/dist/forms/select/Select.svelte +50 -50
  19. package/dist/forms/textarea/Textarea.svelte +165 -165
  20. package/dist/layout/TF/Content/Content.svelte +28 -28
  21. package/dist/layout/TF/Header/Header.svelte +159 -159
  22. package/dist/layout/TF/Sidebar/Sidebar.svelte +74 -74
  23. package/dist/layout/TF/Wrapper/Wrapper.svelte +17 -17
  24. package/dist/themes/ThemeProvider.svelte +20 -20
  25. package/dist/typography/heading/Heading.svelte +35 -35
  26. package/dist/ui/accordion/Accordion.svelte +49 -49
  27. package/dist/ui/accordion/AccordionItem.svelte +173 -173
  28. package/dist/ui/alert/Alert.svelte +83 -83
  29. package/dist/ui/alertDialog/AlertDialog.svelte +40 -40
  30. package/dist/ui/avatar/Avatar.svelte +77 -77
  31. package/dist/ui/buttons/Button.svelte +102 -102
  32. package/dist/ui/buttons/GradientButton.svelte +59 -59
  33. package/dist/ui/datatable/Datatable.svelte +516 -516
  34. package/dist/ui/drawer/Drawer.svelte +280 -280
  35. package/dist/ui/dropdown/Dropdown.svelte +36 -36
  36. package/dist/ui/dropdown/DropdownDivider.svelte +11 -11
  37. package/dist/ui/dropdown/DropdownGroup.svelte +14 -14
  38. package/dist/ui/dropdown/DropdownHeader.svelte +14 -14
  39. package/dist/ui/dropdown/DropdownItem.svelte +52 -52
  40. package/dist/ui/footer/Footer.svelte +15 -15
  41. package/dist/ui/footer/FooterBrand.svelte +37 -37
  42. package/dist/ui/footer/FooterCopyright.svelte +45 -45
  43. package/dist/ui/footer/FooterIcon.svelte +22 -22
  44. package/dist/ui/footer/FooterLink.svelte +33 -33
  45. package/dist/ui/footer/FooterLinkGroup.svelte +13 -13
  46. package/dist/ui/indicator/Indicator.svelte +42 -42
  47. package/dist/ui/modal/Modal.svelte +265 -265
  48. package/dist/ui/notificationList/NotificationList.svelte +123 -123
  49. package/dist/ui/pageLoader/PageLoader.svelte +10 -10
  50. package/dist/ui/paginate/Paginate.svelte +96 -96
  51. package/dist/ui/tab/Tab.svelte +65 -65
  52. package/dist/ui/table/Table.svelte +385 -385
  53. package/dist/ui/tableLoader/TableLoader.svelte +24 -24
  54. package/dist/ui/toolbar/Toolbar.svelte +59 -59
  55. package/dist/ui/toolbar/ToolbarButton.svelte +56 -56
  56. package/dist/ui/toolbar/ToolbarGroup.svelte +43 -43
  57. package/dist/utils/Popper.svelte +257 -257
  58. package/dist/utils/closeButton/CloseButton.svelte +88 -88
  59. package/dist/utils/singleSelection.svelte.js +48 -48
  60. package/dist/youtube/index.svelte +12 -12
  61. package/package.json +7 -3
@@ -1,123 +1,123 @@
1
-
2
-
3
- <script lang="ts" generics="T extends NotificationItem">
4
- import { Dropdown, DropdownItem } from '../dropdown/index.js';
5
- import { Avatar } from '../avatar/index.js';
6
- import { PageLoader, type NotificationItem, type NotificationListProps } from '../../index.js';
7
- import { onDestroy, onMount } from 'svelte';
8
-
9
- let {
10
- open = $bindable(false),
11
- onClearNotifications,
12
- header,
13
- busy = false,
14
- data: initialData = [],
15
- onclick,
16
- footer,
17
- read
18
- }: NotificationListProps<T> = $props();
19
-
20
- let data = $state(initialData);
21
- let internalOpen = $state(false);
22
-
23
- $effect(() => {
24
- if (open !== undefined) {
25
- internalOpen = open;
26
- }
27
- });
28
-
29
- async function fetchData() {
30
- if (!read) return;
31
-
32
- try {
33
- busy = true;
34
- const result = await read();
35
- data = result;
36
- } finally {
37
- busy = false;
38
- }
39
- }
40
-
41
- $effect(() => {
42
- if (internalOpen) {
43
- data = [];
44
- fetchData();
45
- }
46
- });
47
-
48
- onMount(() => {
49
- if (initialData && initialData.length > 0) {
50
- data = initialData;
51
- }
52
- });
53
-
54
- onDestroy(() => {
55
- data = [];
56
- });
57
- </script>
58
-
59
- <Dropdown
60
- bind:isOpen={internalOpen}
61
- class="w-full max-w-sm divide-gray-100 rounded shadow dark:divide-gray-700 dark:bg-gray-800"
62
- >
63
- {#if header}
64
- {@render header()}
65
- {:else}
66
- <div class="py-2 text-center font-bold">Notifications</div>
67
- {/if}
68
- <div
69
- class="scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch flex max-h-80 flex-col divide-y overflow-y-auto"
70
- >
71
- {#if busy}
72
- <div class="flex h-52 w-96 items-center justify-center">
73
- <PageLoader size={40} />
74
- </div>
75
- {:else if data.length}
76
- {#each data as item, index}
77
- <DropdownItem class="flex w-full space-x-4" onclick={() => onclick?.(item)}>
78
- <Avatar cornerStyle="rounded">
79
- <iconify-icon icon={item.icon} style="font-size: 20px;"></iconify-icon>
80
- </Avatar>
81
- <div class="w-full pl-3 text-start">
82
- <div class="mb-1.5 text-sm text-gray-500 dark:text-gray-400">
83
- <span class="font-semibold text-gray-900 dark:text-white">{item.sender}</span>: "{item.content}"
84
- </div>
85
- <div class="flex items-center justify-between">
86
- <div class="text-xs text-primary-600 dark:text-primary-500">{item.time}</div>
87
- <div class="flex items-center gap-1">
88
- {#if item.isRead}
89
- <span class="text-xs font-light text-gray-400">Read</span>
90
- {/if}
91
- <iconify-icon
92
- icon={item.isRead ? 'ri:check-double-line' : 'uim:check'}
93
- class={item.isRead ? 'text-blue-700' : 'text-gray-500'}
94
- style="font-size: 18px;"
95
- ></iconify-icon>
96
- </div>
97
- </div>
98
- </div>
99
- </DropdownItem>
100
- {/each}
101
- {:else}
102
- <div class="flex h-32 w-80 items-center justify-center space-x-4">
103
- <p>No Notifications</p>
104
- </div>
105
- {/if}
106
- </div>
107
- {#if footer}
108
- {@render footer()}
109
- {:else}
110
- <div
111
- class="-my-1 block bg-gray-50 py-2 text-center text-sm font-medium text-gray-900 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700"
112
- >
113
- {#if data.length}
114
- <button class="w-full" onclick={onClearNotifications}>
115
- <div class="inline-flex items-center gap-2">
116
- <iconify-icon icon="ic:round-close"></iconify-icon>
117
- Clear all
118
- </div>
119
- </button>
120
- {/if}
121
- </div>
122
- {/if}
123
- </Dropdown>
1
+
2
+
3
+ <script lang="ts" generics="T extends NotificationItem">
4
+ import { Dropdown, DropdownItem } from '../dropdown/index.js';
5
+ import { Avatar } from '../avatar/index.js';
6
+ import { PageLoader, type NotificationItem, type NotificationListProps } from '../../index.js';
7
+ import { onDestroy, onMount } from 'svelte';
8
+
9
+ let {
10
+ open = $bindable(false),
11
+ onClearNotifications,
12
+ header,
13
+ busy = false,
14
+ data: initialData = [],
15
+ onclick,
16
+ footer,
17
+ read
18
+ }: NotificationListProps<T> = $props();
19
+
20
+ let data = $state(initialData);
21
+ let internalOpen = $state(false);
22
+
23
+ $effect(() => {
24
+ if (open !== undefined) {
25
+ internalOpen = open;
26
+ }
27
+ });
28
+
29
+ async function fetchData() {
30
+ if (!read) return;
31
+
32
+ try {
33
+ busy = true;
34
+ const result = await read();
35
+ data = result;
36
+ } finally {
37
+ busy = false;
38
+ }
39
+ }
40
+
41
+ $effect(() => {
42
+ if (internalOpen) {
43
+ data = [];
44
+ fetchData();
45
+ }
46
+ });
47
+
48
+ onMount(() => {
49
+ if (initialData && initialData.length > 0) {
50
+ data = initialData;
51
+ }
52
+ });
53
+
54
+ onDestroy(() => {
55
+ data = [];
56
+ });
57
+ </script>
58
+
59
+ <Dropdown
60
+ bind:isOpen={internalOpen}
61
+ class="w-full max-w-sm divide-gray-100 rounded shadow dark:divide-gray-700 dark:bg-gray-800"
62
+ >
63
+ {#if header}
64
+ {@render header()}
65
+ {:else}
66
+ <div class="py-2 text-center font-bold">Notifications</div>
67
+ {/if}
68
+ <div
69
+ class="scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch flex max-h-80 flex-col divide-y overflow-y-auto"
70
+ >
71
+ {#if busy}
72
+ <div class="flex h-52 w-96 items-center justify-center">
73
+ <PageLoader size={40} />
74
+ </div>
75
+ {:else if data.length}
76
+ {#each data as item, index}
77
+ <DropdownItem class="flex w-full space-x-4" onclick={() => onclick?.(item)}>
78
+ <Avatar cornerStyle="rounded">
79
+ <iconify-icon icon={item.icon} style="font-size: 20px;"></iconify-icon>
80
+ </Avatar>
81
+ <div class="w-full pl-3 text-start">
82
+ <div class="mb-1.5 text-sm text-gray-500 dark:text-gray-400">
83
+ <span class="font-semibold text-gray-900 dark:text-white">{item.sender}</span>: "{item.content}"
84
+ </div>
85
+ <div class="flex items-center justify-between">
86
+ <div class="text-xs text-primary-600 dark:text-primary-500">{item.time}</div>
87
+ <div class="flex items-center gap-1">
88
+ {#if item.isRead}
89
+ <span class="text-xs font-light text-gray-400">Read</span>
90
+ {/if}
91
+ <iconify-icon
92
+ icon={item.isRead ? 'ri:check-double-line' : 'uim:check'}
93
+ class={item.isRead ? 'text-blue-700' : 'text-gray-500'}
94
+ style="font-size: 18px;"
95
+ ></iconify-icon>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </DropdownItem>
100
+ {/each}
101
+ {:else}
102
+ <div class="flex h-32 w-80 items-center justify-center space-x-4">
103
+ <p>No Notifications</p>
104
+ </div>
105
+ {/if}
106
+ </div>
107
+ {#if footer}
108
+ {@render footer()}
109
+ {:else}
110
+ <div
111
+ class="-my-1 block bg-gray-50 py-2 text-center text-sm font-medium text-gray-900 hover:bg-gray-100 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700"
112
+ >
113
+ {#if data.length}
114
+ <button class="w-full" onclick={onClearNotifications}>
115
+ <div class="inline-flex items-center gap-2">
116
+ <iconify-icon icon="ic:round-close"></iconify-icon>
117
+ Clear all
118
+ </div>
119
+ </button>
120
+ {/if}
121
+ </div>
122
+ {/if}
123
+ </Dropdown>
@@ -1,10 +1,10 @@
1
- <script lang="ts">
2
- import type { PageLoaderProps } from '../../index.js';
3
-
4
- let { size = 70, iconColor = 'text-blue-400' }: PageLoaderProps = $props();
5
- </script>
6
-
7
- <div class="grid h-full w-full items-center justify-center">
8
- <iconify-icon class={iconColor} icon="svg-spinners:blocks-shuffle-3" style="font-size: {size}px;"
9
- ></iconify-icon>
10
- </div>
1
+ <script lang="ts">
2
+ import type { PageLoaderProps } from '../../index.js';
3
+
4
+ let { size = 70, iconColor = 'text-blue-400' }: PageLoaderProps = $props();
5
+ </script>
6
+
7
+ <div class="grid h-full w-full items-center justify-center">
8
+ <iconify-icon class={iconColor} icon="svg-spinners:blocks-shuffle-3" style="font-size: {size}px;"
9
+ ></iconify-icon>
10
+ </div>
@@ -1,96 +1,96 @@
1
- <script lang="ts">
2
- import { Dropdown, type PaginateProps } from '../../index.js';
3
-
4
- let {
5
- currentPage,
6
- hasNextPage,
7
- hasPreviousPage,
8
- onNextPage,
9
- onPreviousPage,
10
- refresh = null,
11
- totalPages,
12
- hiddenColumns = $bindable([]),
13
- tableColumns = [],
14
- textSmall = false,
15
- hideLabel = true,
16
- recordCount = 0
17
- }: PaginateProps = $props();
18
-
19
- // let hideState = tableColumns.map(x => )
20
- let hideState = $state<Record<string, boolean>>({});
21
- hiddenColumns.forEach((x) => (hideState[x] = true));
22
- // $: hiddenColumns = Object.keys(hideState).filter((x) => hideState[x]);
23
- // let hiddenColumns
24
-
25
- let open = $state(false);
26
- </script>
27
-
28
- <div class:text-sm={textSmall} class="flex w-full items-center justify-between gap-2 text-gray-600">
29
- <button
30
- class=" items-center justify-center gap-1 p-2 text-sm font-semibold"
31
- class:hidden={!totalPages}
32
- class:flex={totalPages}
33
- onclick={onPreviousPage}
34
- disabled={!hasPreviousPage}
35
- class:active={hasPreviousPage}
36
- class:hover:bg-gray-200={hasPreviousPage}
37
- class:rounded-md={hasPreviousPage}
38
- class:text-gray-300={!hasPreviousPage}
39
- >
40
- <iconify-icon icon="material-symbols:arrow-back" style="font-size: 18px"></iconify-icon>
41
- {#if !hideLabel}
42
- <span>PREV</span>
43
- {/if}
44
- </button>
45
- <div class="text-sm font-semibold">
46
- {#if !hideLabel}
47
- <span>Page</span>
48
- {/if}
49
- <span class:hidden={!totalPages} class:flex={totalPages}>{currentPage}/{totalPages}</span>
50
- </div>
51
- <span class:hidden={!recordCount} class:flex={recordCount}>({recordCount})</span>
52
-
53
- <button
54
- class="flex items-center justify-center gap-1 p-2 text-sm font-semibold"
55
- class:hidden={!totalPages}
56
- class:flex={totalPages}
57
- onclick={onNextPage}
58
- disabled={!hasNextPage}
59
- class:active={hasNextPage}
60
- class:hover:bg-gray-200={hasNextPage}
61
- class:rounded-md={hasNextPage}
62
- class:text-gray-300={!hasNextPage}
63
- >
64
- {#if !hideLabel}
65
- <span>NEXT</span>
66
- {/if}
67
- <iconify-icon icon="material-symbols:arrow-forward" style="font-size: 18px"></iconify-icon>
68
- </button>
69
- <button
70
- type="button"
71
- aria-label="arrows-clockwise"
72
- class="rotate-2 font-bold hover:text-gray-600"
73
- class:hidden={!refresh}
74
- onclick={refresh}
75
- >
76
- <iconify-icon icon="ph:arrows-clockwise" class="hover:animate-spin hover:text-gray-400"
77
- ></iconify-icon>
78
- </button>
79
- <div class:hidden={!tableColumns.length} class:flex={tableColumns}>
80
- <button onclick={() => (open = !open)}>
81
- <iconify-icon icon="ph:columns" class="text-red-500 hover:text-red-800"></iconify-icon>
82
- <!-- <Tooltip>Toggle Columns</Tooltip> -->
83
- </button>
84
- <Dropdown
85
- bind:isOpen={open}
86
- class="scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch h-56 min-w-40 overflow-y-auto px-3 pb-3 text-sm"
87
- >
88
- {#each tableColumns as column}
89
- <li class="flex w-full px-2 py-1.5 whitespace-nowrap">
90
- <!-- <Toggle bind:checked={hideState[column.id]} class="cursor-pointer" /> -->
91
- {column.header}
92
- </li>
93
- {/each}
94
- </Dropdown>
95
- </div>
96
- </div>
1
+ <script lang="ts">
2
+ import { Dropdown, type PaginateProps } from '../../index.js';
3
+
4
+ let {
5
+ currentPage,
6
+ hasNextPage,
7
+ hasPreviousPage,
8
+ onNextPage,
9
+ onPreviousPage,
10
+ refresh = null,
11
+ totalPages,
12
+ hiddenColumns = $bindable([]),
13
+ tableColumns = [],
14
+ textSmall = false,
15
+ hideLabel = true,
16
+ recordCount = 0
17
+ }: PaginateProps = $props();
18
+
19
+ // let hideState = tableColumns.map(x => )
20
+ let hideState = $state<Record<string, boolean>>({});
21
+ hiddenColumns.forEach((x) => (hideState[x] = true));
22
+ // $: hiddenColumns = Object.keys(hideState).filter((x) => hideState[x]);
23
+ // let hiddenColumns
24
+
25
+ let open = $state(false);
26
+ </script>
27
+
28
+ <div class:text-sm={textSmall} class="flex w-full items-center justify-between gap-2 text-gray-600">
29
+ <button
30
+ class=" items-center justify-center gap-1 p-2 text-sm font-semibold"
31
+ class:hidden={!totalPages}
32
+ class:flex={totalPages}
33
+ onclick={onPreviousPage}
34
+ disabled={!hasPreviousPage}
35
+ class:active={hasPreviousPage}
36
+ class:hover:bg-gray-200={hasPreviousPage}
37
+ class:rounded-md={hasPreviousPage}
38
+ class:text-gray-300={!hasPreviousPage}
39
+ >
40
+ <iconify-icon icon="material-symbols:arrow-back" style="font-size: 18px"></iconify-icon>
41
+ {#if !hideLabel}
42
+ <span>PREV</span>
43
+ {/if}
44
+ </button>
45
+ <div class="text-sm font-semibold">
46
+ {#if !hideLabel}
47
+ <span>Page</span>
48
+ {/if}
49
+ <span class:hidden={!totalPages} class:flex={totalPages}>{currentPage}/{totalPages}</span>
50
+ </div>
51
+ <span class:hidden={!recordCount} class:flex={recordCount}>({recordCount})</span>
52
+
53
+ <button
54
+ class="flex items-center justify-center gap-1 p-2 text-sm font-semibold"
55
+ class:hidden={!totalPages}
56
+ class:flex={totalPages}
57
+ onclick={onNextPage}
58
+ disabled={!hasNextPage}
59
+ class:active={hasNextPage}
60
+ class:hover:bg-gray-200={hasNextPage}
61
+ class:rounded-md={hasNextPage}
62
+ class:text-gray-300={!hasNextPage}
63
+ >
64
+ {#if !hideLabel}
65
+ <span>NEXT</span>
66
+ {/if}
67
+ <iconify-icon icon="material-symbols:arrow-forward" style="font-size: 18px"></iconify-icon>
68
+ </button>
69
+ <button
70
+ type="button"
71
+ aria-label="arrows-clockwise"
72
+ class="rotate-2 font-bold hover:text-gray-600"
73
+ class:hidden={!refresh}
74
+ onclick={refresh}
75
+ >
76
+ <iconify-icon icon="ph:arrows-clockwise" class="hover:animate-spin hover:text-gray-400"
77
+ ></iconify-icon>
78
+ </button>
79
+ <div class:hidden={!tableColumns.length} class:flex={tableColumns}>
80
+ <button onclick={() => (open = !open)}>
81
+ <iconify-icon icon="ph:columns" class="text-red-500 hover:text-red-800"></iconify-icon>
82
+ <!-- <Tooltip>Toggle Columns</Tooltip> -->
83
+ </button>
84
+ <Dropdown
85
+ bind:isOpen={open}
86
+ class="scrollbar-thumb-blue scrollbar-thumb-rounded scrollbar-track-blue-lighter scrollbar-w-2 scrolling-touch h-56 min-w-40 overflow-y-auto px-3 pb-3 text-sm"
87
+ >
88
+ {#each tableColumns as column}
89
+ <li class="flex w-full px-2 py-1.5 whitespace-nowrap">
90
+ <!-- <Toggle bind:checked={hideState[column.id]} class="cursor-pointer" /> -->
91
+ {column.header}
92
+ </li>
93
+ {/each}
94
+ </Dropdown>
95
+ </div>
96
+ </div>
@@ -1,65 +1,65 @@
1
- <script lang="ts">
2
- import { CloseButton, type TabProps } from '../../index.js';
3
-
4
- let {
5
- activeTab = $bindable(0),
6
- tabs = [],
7
- onTabChange,
8
- onRemoveTab,
9
- onTabAdd
10
- }: TabProps = $props();
11
-
12
- function changeTab(id: number | string) {
13
- activeTab = id;
14
- onTabChange?.(id);
15
- }
16
- </script>
17
-
18
- <div class="relative flex h-full w-full flex-col">
19
- <div class="mb-4 flex flex-wrap space-x-3 border-b-2">
20
- {#each tabs as tab}
21
- <button
22
- class="rounded-t-md border-b-2 px-4 py-2"
23
- class:bg-primary-500={activeTab === tab.id}
24
- class:text-white={activeTab === tab.id}
25
- class:text-gray-600={activeTab !== tab.id}
26
- class:hover:bg-gray-100={activeTab !== tab.id}
27
- class:border-transparent={activeTab !== tab.id}
28
- class:border-white={activeTab === tab.id}
29
- onclick={() => changeTab(tab.id)}
30
- >
31
- <div class="flex items-center gap-2">
32
- <span class="text-sm">
33
- {tab.title}
34
- </span>
35
- <iconify-icon icon={tab.icon} style="font-size: 15px;"></iconify-icon>
36
- {#if tab.isClosable}
37
- <CloseButton
38
- size="sm"
39
- class=" {activeTab === tab.id
40
- ? 'text-white hover:bg-white/20'
41
- : 'text-gray-600 hover:bg-gray-200'}"
42
- onclick={(e: any) => {
43
- onRemoveTab?.({ tabId: tab.id });
44
- e.stopPropagation();
45
- }}
46
- />
47
- {/if}
48
- </div>
49
- </button>
50
- {/each}
51
- </div>
52
-
53
- <div class=" h-full w-full">
54
- {#each tabs as tab (tab.id)}
55
- <div class:hidden={activeTab !== tab.id} class="h-full w-full">
56
- <!-- {@render tab.children({
57
- ...tab,
58
- onTabAdd,
59
- onRemoveTab
60
- })} -->
61
- <tab.children {...tab} {onTabAdd} {onRemoveTab} />
62
- </div>
63
- {/each}
64
- </div>
65
- </div>
1
+ <script lang="ts">
2
+ import { CloseButton, type TabProps } from '../../index.js';
3
+
4
+ let {
5
+ activeTab = $bindable(0),
6
+ tabs = [],
7
+ onTabChange,
8
+ onRemoveTab,
9
+ onTabAdd
10
+ }: TabProps = $props();
11
+
12
+ function changeTab(id: number | string) {
13
+ activeTab = id;
14
+ onTabChange?.(id);
15
+ }
16
+ </script>
17
+
18
+ <div class="relative flex h-full w-full flex-col">
19
+ <div class="mb-4 flex flex-wrap space-x-3 border-b-2">
20
+ {#each tabs as tab}
21
+ <button
22
+ class="rounded-t-md border-b-2 px-4 py-2"
23
+ class:bg-primary-500={activeTab === tab.id}
24
+ class:text-white={activeTab === tab.id}
25
+ class:text-gray-600={activeTab !== tab.id}
26
+ class:hover:bg-gray-100={activeTab !== tab.id}
27
+ class:border-transparent={activeTab !== tab.id}
28
+ class:border-white={activeTab === tab.id}
29
+ onclick={() => changeTab(tab.id)}
30
+ >
31
+ <div class="flex items-center gap-2">
32
+ <span class="text-sm">
33
+ {tab.title}
34
+ </span>
35
+ <iconify-icon icon={tab.icon} style="font-size: 15px;"></iconify-icon>
36
+ {#if tab.isClosable}
37
+ <CloseButton
38
+ size="sm"
39
+ class=" {activeTab === tab.id
40
+ ? 'text-white hover:bg-white/20'
41
+ : 'text-gray-600 hover:bg-gray-200'}"
42
+ onclick={(e: any) => {
43
+ onRemoveTab?.({ tabId: tab.id });
44
+ e.stopPropagation();
45
+ }}
46
+ />
47
+ {/if}
48
+ </div>
49
+ </button>
50
+ {/each}
51
+ </div>
52
+
53
+ <div class=" h-full w-full">
54
+ {#each tabs as tab (tab.id)}
55
+ <div class:hidden={activeTab !== tab.id} class="h-full w-full">
56
+ <!-- {@render tab.children({
57
+ ...tab,
58
+ onTabAdd,
59
+ onRemoveTab
60
+ })} -->
61
+ <tab.children {...tab} {onTabAdd} {onRemoveTab} />
62
+ </div>
63
+ {/each}
64
+ </div>
65
+ </div>