@rainlanguage/ui-components 0.0.1-alpha.196 → 0.0.1-alpha.200

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 (59) hide show
  1. package/dist/__mocks__/stores.d.ts +15 -0
  2. package/dist/__mocks__/stores.js +18 -0
  3. package/dist/components/CheckboxInactiveOrdersVault.svelte +20 -0
  4. package/dist/components/CheckboxInactiveOrdersVault.svelte.d.ts +19 -0
  5. package/dist/components/ListViewOrderbookFilters.svelte +14 -0
  6. package/dist/components/ListViewOrderbookFilters.svelte.d.ts +3 -0
  7. package/dist/components/LocalDbStatusBadge.svelte +27 -0
  8. package/dist/components/LocalDbStatusBadge.svelte.d.ts +19 -0
  9. package/dist/components/LocalDbStatusCard.svelte +40 -0
  10. package/dist/components/LocalDbStatusCard.svelte.d.ts +20 -0
  11. package/dist/components/LocalDbStatusModal.svelte +103 -0
  12. package/dist/components/LocalDbStatusModal.svelte.d.ts +21 -0
  13. package/dist/components/TanstackAppTable.svelte +176 -29
  14. package/dist/components/TanstackAppTable.svelte.d.ts +5 -0
  15. package/dist/components/VaultBalanceChangeTypeFilter.svelte +23 -0
  16. package/dist/components/VaultBalanceChangeTypeFilter.svelte.d.ts +19 -0
  17. package/dist/components/VaultCard.svelte +2 -2
  18. package/dist/components/charts/OrderTradesChart.svelte +295 -13
  19. package/dist/components/charts/OrderTradesChart.svelte.d.ts +2 -2
  20. package/dist/components/deployment/TokenSelectionModal.svelte +20 -0
  21. package/dist/components/deployment/TokenSelectionModal.svelte.d.ts +3 -3
  22. package/dist/components/detail/OrderDetail.svelte +20 -6
  23. package/dist/components/detail/OrderDetail.svelte.d.ts +0 -1
  24. package/dist/components/detail/TanstackOrderQuote.svelte +25 -7
  25. package/dist/components/detail/VaultDetail.svelte +20 -2
  26. package/dist/components/dropdown/DropdownActiveNetworks.svelte +6 -11
  27. package/dist/components/dropdown/DropdownCheckbox.svelte +2 -2
  28. package/dist/components/dropdown/DropdownOrderbooksFilter.svelte +172 -0
  29. package/dist/components/dropdown/DropdownOrderbooksFilter.svelte.d.ts +25 -0
  30. package/dist/components/dropdown/DropdownTokensFilter.svelte +13 -10
  31. package/dist/components/tables/OrderTradesListTable.svelte +65 -33
  32. package/dist/components/tables/OrderVaultsVolTable.svelte +26 -37
  33. package/dist/components/tables/OrderVaultsVolTable.svelte.d.ts +4 -1
  34. package/dist/components/tables/OrdersListTable.svelte +64 -75
  35. package/dist/components/tables/OrdersListTable.svelte.d.ts +2 -1
  36. package/dist/components/tables/VaultBalanceChangesTable.svelte +76 -28
  37. package/dist/components/tables/VaultsListTable.svelte +75 -31
  38. package/dist/components/tables/VaultsListTable.svelte.d.ts +2 -0
  39. package/dist/index.d.ts +9 -0
  40. package/dist/index.js +8 -0
  41. package/dist/models/Transaction.d.ts +4 -8
  42. package/dist/models/Transaction.js +17 -49
  43. package/dist/providers/dotrainRegistry/DotrainRegistryProvider.svelte +8 -0
  44. package/dist/providers/dotrainRegistry/DotrainRegistryProvider.svelte.d.ts +24 -0
  45. package/dist/providers/dotrainRegistry/context.d.ts +9 -0
  46. package/dist/providers/dotrainRegistry/context.js +40 -0
  47. package/dist/providers/dotrainRegistry/useDotrainRegistry.d.ts +5 -0
  48. package/dist/providers/dotrainRegistry/useDotrainRegistry.js +35 -0
  49. package/dist/providers/transactions/TransactionManager.d.ts +20 -4
  50. package/dist/providers/transactions/TransactionManager.js +99 -46
  51. package/dist/services/getExplorerLink.d.ts +1 -1
  52. package/dist/services/getExplorerLink.js +2 -4
  53. package/dist/services/pairTradesChartData.d.ts +47 -0
  54. package/dist/services/pairTradesChartData.js +190 -0
  55. package/dist/types/appStores.d.ts +2 -0
  56. package/dist/types/transaction.d.ts +23 -2
  57. package/dist/utils/vaultBalanceChangeLabels.d.ts +4 -0
  58. package/dist/utils/vaultBalanceChangeLabels.js +18 -0
  59. package/package.json +7 -2
@@ -36,6 +36,11 @@ export declare const mockHideZeroBalanceVaultsStore: {
36
36
  set: (this: void, value: boolean) => void;
37
37
  mockSetSubscribeValue: (value: boolean) => void;
38
38
  };
39
+ export declare const mockHideInactiveOrdersVaultsStore: {
40
+ subscribe: (this: void, run: import("svelte/store").Subscriber<boolean>, invalidate?: import("svelte/store").Invalidator<boolean> | undefined) => import("svelte/store").Unsubscriber;
41
+ set: (this: void, value: boolean) => void;
42
+ mockSetSubscribeValue: (value: boolean) => void;
43
+ };
39
44
  export declare const mockActiveNetworkRefStore: {
40
45
  subscribe: (this: void, run: import("svelte/store").Subscriber<string>, invalidate?: import("svelte/store").Invalidator<string> | undefined) => import("svelte/store").Unsubscriber;
41
46
  set: (this: void, value: string) => void;
@@ -137,4 +142,14 @@ export declare const mockPageStore: {
137
142
  mockSetSubscribeValue: (newValue: Partial<typeof initialPageState>) => void;
138
143
  reset: () => void;
139
144
  };
145
+ export declare const mockActiveTokensStore: {
146
+ subscribe: (this: void, run: import("svelte/store").Subscriber<string[]>, invalidate?: import("svelte/store").Invalidator<string[]> | undefined) => import("svelte/store").Unsubscriber;
147
+ set: (this: void, value: string[]) => void;
148
+ mockSetSubscribeValue: (value: string[]) => void;
149
+ };
150
+ export declare const mockActiveOrderbookAddressesStore: {
151
+ subscribe: (this: void, run: import("svelte/store").Subscriber<string[]>, invalidate?: import("svelte/store").Invalidator<string[]> | undefined) => import("svelte/store").Unsubscriber;
152
+ set: (this: void, value: string[]) => void;
153
+ mockSetSubscribeValue: (value: string[]) => void;
154
+ };
140
155
  export {};
@@ -29,6 +29,7 @@ const mockActiveAccountsItemsWritable = writable({});
29
29
  const mockShowInactiveOrdersWritable = writable(true);
30
30
  const mockOrderHashWritable = writable('');
31
31
  const mockHideZeroBalanceVaultsWritable = writable(false);
32
+ const mockHideInactiveOrdersVaultsWritable = writable(false);
32
33
  const mockActiveNetworkRefWritable = writable('');
33
34
  const mockActiveOrderbookRefWritable = writable('');
34
35
  const mockActiveAccountsWritable = writable({});
@@ -38,6 +39,8 @@ const mockConnectedWritable = writable(true);
38
39
  const mockWagmiConfigWritable = writable(mockWeb3Config);
39
40
  const mockShowMyItemsOnlyWritable = writable(false);
40
41
  const mockSelectedChainIdsWritable = writable([]);
42
+ const mockActiveTokensWritable = writable([]);
43
+ const mockActiveOrderbookAddressesWritable = writable([]);
41
44
  export const mockActiveAccountsItemsStore = {
42
45
  subscribe: mockActiveAccountsItemsWritable.subscribe,
43
46
  set: mockActiveAccountsItemsWritable.set,
@@ -58,6 +61,11 @@ export const mockHideZeroBalanceVaultsStore = {
58
61
  set: mockHideZeroBalanceVaultsWritable.set,
59
62
  mockSetSubscribeValue: (value) => mockHideZeroBalanceVaultsWritable.set(value)
60
63
  };
64
+ export const mockHideInactiveOrdersVaultsStore = {
65
+ subscribe: mockHideInactiveOrdersVaultsWritable.subscribe,
66
+ set: mockHideInactiveOrdersVaultsWritable.set,
67
+ mockSetSubscribeValue: (value) => mockHideInactiveOrdersVaultsWritable.set(value)
68
+ };
61
69
  export const mockActiveNetworkRefStore = {
62
70
  subscribe: mockActiveNetworkRefWritable.subscribe,
63
71
  set: mockActiveNetworkRefWritable.set,
@@ -116,3 +124,13 @@ export const mockPageStore = {
116
124
  },
117
125
  reset: () => mockPageWritable.set(initialPageState)
118
126
  };
127
+ export const mockActiveTokensStore = {
128
+ subscribe: mockActiveTokensWritable.subscribe,
129
+ set: mockActiveTokensWritable.set,
130
+ mockSetSubscribeValue: (value) => mockActiveTokensWritable.set(value)
131
+ };
132
+ export const mockActiveOrderbookAddressesStore = {
133
+ subscribe: mockActiveOrderbookAddressesWritable.subscribe,
134
+ set: mockActiveOrderbookAddressesWritable.set,
135
+ mockSetSubscribeValue: (value) => mockActiveOrderbookAddressesWritable.set(value)
136
+ };
@@ -0,0 +1,20 @@
1
+ <script>import { Checkbox, Label } from "flowbite-svelte";
2
+ export let hideInactiveOrdersVaults;
3
+ function handleHideInactiveOrdersVaultsChange() {
4
+ $hideInactiveOrdersVaults = !$hideInactiveOrdersVaults;
5
+ }
6
+ </script>
7
+
8
+ <div data-testid="inactive-orders-vault-checkbox" class="flex items-center gap-x-2">
9
+ <Label
10
+ for="hide-inactive-orders-vaults"
11
+ class="cursor-pointer whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-300"
12
+ >
13
+ Hide vaults without active orders
14
+ </Label>
15
+ <Checkbox
16
+ id="hide-inactive-orders-vaults"
17
+ checked={$hideInactiveOrdersVaults}
18
+ on:change={handleHideInactiveOrdersVaultsChange}
19
+ />
20
+ </div>
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { AppStoresInterface } from '../types/appStores';
3
+ declare const __propDef: {
4
+ props: {
5
+ hideInactiveOrdersVaults: AppStoresInterface["hideInactiveOrdersVaults"];
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ exports?: {} | undefined;
12
+ bindings?: string | undefined;
13
+ };
14
+ export type CheckboxInactiveOrdersVaultProps = typeof __propDef.props;
15
+ export type CheckboxInactiveOrdersVaultEvents = typeof __propDef.events;
16
+ export type CheckboxInactiveOrdersVaultSlots = typeof __propDef.slots;
17
+ export default class CheckboxInactiveOrdersVault extends SvelteComponent<CheckboxInactiveOrdersVaultProps, CheckboxInactiveOrdersVaultEvents, CheckboxInactiveOrdersVaultSlots> {
18
+ }
19
+ export {};
@@ -7,11 +7,14 @@ import Tooltip from "./Tooltip.svelte";
7
7
  import CheckboxActiveOrders from "./checkbox/CheckboxActiveOrders.svelte";
8
8
  import DropdownOrderListAccounts from "./dropdown/DropdownOrderListAccounts.svelte";
9
9
  import DropdownTokensFilter from "./dropdown/DropdownTokensFilter.svelte";
10
+ import DropdownOrderbooksFilter from "./dropdown/DropdownOrderbooksFilter.svelte";
10
11
  import InputOrderHash from "./input/InputOrderHash.svelte";
11
12
  import CheckboxZeroBalanceVault from "./CheckboxZeroBalanceVault.svelte";
13
+ import CheckboxInactiveOrdersVault from "./CheckboxInactiveOrdersVault.svelte";
12
14
  import CheckboxMyItemsOnly from "./CheckboxMyItemsOnly.svelte";
13
15
  import { useAccount } from "../providers/wallet/useAccount";
14
16
  export let hideZeroBalanceVaults;
17
+ export let hideInactiveOrdersVaults;
15
18
  export let activeAccountsItems;
16
19
  export let showMyItemsOnly;
17
20
  export let selectedChainIds;
@@ -20,6 +23,8 @@ export let orderHash;
20
23
  export let activeTokens;
21
24
  export let selectedTokens;
22
25
  export let tokensQuery;
26
+ export let activeOrderbookAddresses;
27
+ export let selectedOrderbookAddresses;
23
28
  $: isVaultsPage = $page.url.pathname === "/vaults";
24
29
  $: isOrdersPage = $page.url.pathname === "/orders";
25
30
  const { account } = useAccount();
@@ -49,6 +54,9 @@ $: accounts = raindexClient.getAllAccounts();
49
54
  <div class="mt-4 w-full lg:w-auto">
50
55
  <CheckboxZeroBalanceVault {hideZeroBalanceVaults} />
51
56
  </div>
57
+ <div class="mt-4 w-full lg:w-auto">
58
+ <CheckboxInactiveOrdersVault {hideInactiveOrdersVaults} />
59
+ </div>
52
60
  {/if}
53
61
 
54
62
  {#if isOrdersPage}
@@ -61,6 +69,12 @@ $: accounts = raindexClient.getAllAccounts();
61
69
  <DropdownOrderListAccounts {activeAccountsItems} />
62
70
  {/if}
63
71
  <DropdownTokensFilter {tokensQuery} {activeTokens} {selectedTokens} label="Tokens" />
72
+ <DropdownOrderbooksFilter
73
+ {activeOrderbookAddresses}
74
+ {selectedOrderbookAddresses}
75
+ selectedChainIds={$selectedChainIds}
76
+ label="Orderbooks"
77
+ />
64
78
  <DropdownActiveNetworks {selectedChainIds} />
65
79
  {/if}
66
80
  </div>
@@ -6,6 +6,7 @@ import type { AppStoresInterface } from '../types/appStores';
6
6
  declare const __propDef: {
7
7
  props: {
8
8
  hideZeroBalanceVaults: AppStoresInterface["hideZeroBalanceVaults"];
9
+ hideInactiveOrdersVaults: AppStoresInterface["hideInactiveOrdersVaults"];
9
10
  activeAccountsItems: AppStoresInterface["activeAccountsItems"];
10
11
  showMyItemsOnly: AppStoresInterface["showMyItemsOnly"];
11
12
  selectedChainIds: AppStoresInterface["selectedChainIds"];
@@ -14,6 +15,8 @@ declare const __propDef: {
14
15
  activeTokens: AppStoresInterface["activeTokens"];
15
16
  selectedTokens: Address[];
16
17
  tokensQuery: Readable<QueryObserverResult<RaindexVaultToken[], Error>>;
18
+ activeOrderbookAddresses: AppStoresInterface["activeOrderbookAddresses"];
19
+ selectedOrderbookAddresses: Address[];
17
20
  };
18
21
  events: {
19
22
  [evt: string]: CustomEvent<any>;
@@ -0,0 +1,27 @@
1
+ <script>const STATUS_LABELS = {
2
+ active: "Active",
3
+ syncing: "Syncing",
4
+ failure: "Failure"
5
+ };
6
+ const DOT_CLASSES = {
7
+ active: "bg-emerald-400",
8
+ syncing: "bg-sky-400",
9
+ failure: "bg-red-500"
10
+ };
11
+ const TEXT_CLASSES = {
12
+ active: "text-emerald-400",
13
+ syncing: "text-sky-400",
14
+ failure: "text-red-400"
15
+ };
16
+ export let status = "active";
17
+ </script>
18
+
19
+ <span
20
+ class="inline-flex items-center gap-2 rounded-full border border-gray-200 bg-gray-100 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-gray-600 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300"
21
+ data-testid="local-db-status"
22
+ >
23
+ <span class={`h-2 w-2 rounded-full ${DOT_CLASSES[status]}`} aria-hidden="true" />
24
+ <span class={TEXT_CLASSES[status]}>
25
+ {STATUS_LABELS[status]}
26
+ </span>
27
+ </span>
@@ -0,0 +1,19 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { LocalDbStatus } from '@rainlanguage/orderbook';
3
+ declare const __propDef: {
4
+ props: {
5
+ status?: LocalDbStatus;
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ exports?: {} | undefined;
12
+ bindings?: string | undefined;
13
+ };
14
+ export type LocalDbStatusBadgeProps = typeof __propDef.props;
15
+ export type LocalDbStatusBadgeEvents = typeof __propDef.events;
16
+ export type LocalDbStatusBadgeSlots = typeof __propDef.slots;
17
+ export default class LocalDbStatusBadge extends SvelteComponent<LocalDbStatusBadgeProps, LocalDbStatusBadgeEvents, LocalDbStatusBadgeSlots> {
18
+ }
19
+ export {};
@@ -0,0 +1,40 @@
1
+ <script>import LocalDbStatusBadge from "./LocalDbStatusBadge.svelte";
2
+ import LocalDbStatusModal from "./LocalDbStatusModal.svelte";
3
+ import { ChevronRightOutline } from "flowbite-svelte-icons";
4
+ export let networkStatuses = /* @__PURE__ */ new Map();
5
+ export let orderbookStatuses = /* @__PURE__ */ new Map();
6
+ let modalOpen = false;
7
+ $: networkList = Array.from(networkStatuses.values());
8
+ $: hasNetworks = networkList.length > 0;
9
+ $: hasFailure = networkList.some((s) => s.status === "failure");
10
+ $: displayStatus = hasFailure ? "failure" : "active";
11
+ function openModal() {
12
+ modalOpen = true;
13
+ }
14
+ </script>
15
+
16
+ <div
17
+ class="rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900"
18
+ data-testid="local-db-status-card"
19
+ >
20
+ <button
21
+ type="button"
22
+ class="flex w-full items-center justify-between px-3 py-3 text-left transition hover:bg-gray-50 dark:hover:bg-gray-800"
23
+ on:click={openModal}
24
+ data-testid="local-db-status-header"
25
+ >
26
+ <div class="flex items-center gap-2">
27
+ <span class="text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"
28
+ >LocalDB</span
29
+ >
30
+ </div>
31
+ <div class="flex items-center gap-2">
32
+ <LocalDbStatusBadge status={displayStatus} />
33
+ {#if hasNetworks}
34
+ <ChevronRightOutline class="h-3 w-3 shrink-0" />
35
+ {/if}
36
+ </div>
37
+ </button>
38
+ </div>
39
+
40
+ <LocalDbStatusModal bind:open={modalOpen} {networkStatuses} {orderbookStatuses} />
@@ -0,0 +1,20 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { NetworkSyncStatus, OrderbookSyncStatus } from '@rainlanguage/orderbook';
3
+ declare const __propDef: {
4
+ props: {
5
+ networkStatuses?: Map<number, NetworkSyncStatus>;
6
+ orderbookStatuses?: Map<string, OrderbookSyncStatus>;
7
+ };
8
+ events: {
9
+ [evt: string]: CustomEvent<any>;
10
+ };
11
+ slots: {};
12
+ exports?: {} | undefined;
13
+ bindings?: string | undefined;
14
+ };
15
+ export type LocalDbStatusCardProps = typeof __propDef.props;
16
+ export type LocalDbStatusCardEvents = typeof __propDef.events;
17
+ export type LocalDbStatusCardSlots = typeof __propDef.slots;
18
+ export default class LocalDbStatusCard extends SvelteComponent<LocalDbStatusCardProps, LocalDbStatusCardEvents, LocalDbStatusCardSlots> {
19
+ }
20
+ export {};
@@ -0,0 +1,103 @@
1
+ <script>import { Modal } from "flowbite-svelte";
2
+ import LocalDbStatusBadge from "./LocalDbStatusBadge.svelte";
3
+ import { getNetworkName } from "../utils/getNetworkName";
4
+ export let open = false;
5
+ export let networkStatuses = /* @__PURE__ */ new Map();
6
+ export let orderbookStatuses = /* @__PURE__ */ new Map();
7
+ $: networkList = Array.from(networkStatuses.values());
8
+ $: orderbookList = Array.from(orderbookStatuses.values());
9
+ $: networkGroups = buildNetworkGroups(networkList, orderbookList);
10
+ function buildNetworkGroups(networks, orderbooks) {
11
+ const groups = [];
12
+ for (const network of networks) {
13
+ const networkOrderbooks = orderbooks.filter((ob) => ob.obId.chainId === network.chainId);
14
+ groups.push({
15
+ chainId: network.chainId,
16
+ networkName: getNetworkName(network.chainId) ?? `Chain ${network.chainId}`,
17
+ status: network,
18
+ orderbooks: networkOrderbooks
19
+ });
20
+ }
21
+ return groups;
22
+ }
23
+ </script>
24
+
25
+ <Modal
26
+ bind:open
27
+ size="lg"
28
+ class="dark:border dark:border-gray-700 dark:bg-gray-900"
29
+ dialogClass="fixed top-0 start-0 end-1 h-modal md:inset-0 md:h-full z-50 w-full p-4 flex justify-center items-center h-full"
30
+ data-testid="local-db-status-modal"
31
+ >
32
+ <div class="flex flex-col gap-4" slot="header">
33
+ <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Database Sync Status</h3>
34
+ </div>
35
+
36
+ <div class="max-h-[60vh] overflow-y-auto">
37
+ {#if networkList.length === 0}
38
+ <p class="text-sm text-gray-500 dark:text-gray-400">No networks are being synced.</p>
39
+ {:else}
40
+ <div class="space-y-4">
41
+ {#each networkGroups as group (group.chainId)}
42
+ <div
43
+ class="rounded-lg border border-gray-200 dark:border-gray-700"
44
+ data-testid="network-group-{group.chainId}"
45
+ >
46
+ <div
47
+ class="flex items-center justify-between border-b border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-700 dark:bg-gray-800"
48
+ >
49
+ <div class="flex items-center gap-2">
50
+ <span class="font-medium text-gray-900 dark:text-white">{group.networkName}</span>
51
+ {#if group.status.schedulerState === 'notLeader'}
52
+ <span
53
+ class="rounded bg-amber-100 px-1.5 py-0.5 text-[10px] font-medium uppercase text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"
54
+ >
55
+ Observing
56
+ </span>
57
+ {/if}
58
+ </div>
59
+ <LocalDbStatusBadge status={group.status.status} />
60
+ </div>
61
+
62
+ {#if group.orderbooks.length > 0}
63
+ <ul class="divide-y divide-gray-100 dark:divide-gray-800">
64
+ {#each group.orderbooks as obStatus (obStatus.obId.orderbookAddress)}
65
+ <li class="px-4 py-3">
66
+ <div class="flex items-start justify-between gap-4">
67
+ <div class="min-w-0 flex-1">
68
+ <span
69
+ class="font-mono text-sm text-gray-700 dark:text-gray-300"
70
+ title={obStatus.obId.orderbookAddress}
71
+ >
72
+ {obStatus.obId.orderbookAddress}
73
+ </span>
74
+ {#if obStatus.status === 'syncing' && obStatus.phaseMessage && obStatus.schedulerState !== 'notLeader'}
75
+ <div class="mt-2 text-sm text-sky-600 dark:text-sky-400">
76
+ {obStatus.phaseMessage}
77
+ </div>
78
+ {/if}
79
+ {#if obStatus.status === 'failure' && obStatus.error}
80
+ <div class="mt-2 text-sm text-red-600 dark:text-red-400">
81
+ {obStatus.error}
82
+ </div>
83
+ {/if}
84
+ </div>
85
+ </div>
86
+ </li>
87
+ {/each}
88
+ </ul>
89
+ {/if}
90
+
91
+ {#if group.status.status === 'failure' && group.status.error}
92
+ <div class="border-t border-gray-200 px-4 py-3 dark:border-gray-700">
93
+ <div class="text-sm text-red-600 dark:text-red-400">
94
+ {group.status.error}
95
+ </div>
96
+ </div>
97
+ {/if}
98
+ </div>
99
+ {/each}
100
+ </div>
101
+ {/if}
102
+ </div>
103
+ </Modal>
@@ -0,0 +1,21 @@
1
+ import { SvelteComponent } from "svelte";
2
+ import type { NetworkSyncStatus, OrderbookSyncStatus } from '@rainlanguage/orderbook';
3
+ declare const __propDef: {
4
+ props: {
5
+ open?: boolean;
6
+ networkStatuses?: Map<number, NetworkSyncStatus>;
7
+ orderbookStatuses?: Map<string, OrderbookSyncStatus>;
8
+ };
9
+ events: {
10
+ [evt: string]: CustomEvent<any>;
11
+ };
12
+ slots: {};
13
+ exports?: {} | undefined;
14
+ bindings?: string | undefined;
15
+ };
16
+ export type LocalDbStatusModalProps = typeof __propDef.props;
17
+ export type LocalDbStatusModalEvents = typeof __propDef.events;
18
+ export type LocalDbStatusModalSlots = typeof __propDef.slots;
19
+ export default class LocalDbStatusModal extends SvelteComponent<LocalDbStatusModalProps, LocalDbStatusModalEvents, LocalDbStatusModalSlots> {
20
+ }
21
+ export {};
@@ -1,8 +1,9 @@
1
1
  <script generics="DataItem, InputData = DataItem[]">import { invalidateTanstackQueries } from "../queries/queryClient";
2
2
  import Refresh from "./icon/Refresh.svelte";
3
3
  import { Button, Table, TableBody, TableBodyRow, TableHead } from "flowbite-svelte";
4
- import { createEventDispatcher } from "svelte";
4
+ import { afterUpdate, createEventDispatcher, onDestroy, onMount } from "svelte";
5
5
  import { useQueryClient } from "@tanstack/svelte-query";
6
+ import { createWindowVirtualizer } from "@tanstack/svelte-virtual";
6
7
  const queryClient = useQueryClient();
7
8
  const dispatch = createEventDispatcher();
8
9
  export let queryKey;
@@ -10,10 +11,128 @@ export let query;
10
11
  export let emptyMessage = "None found";
11
12
  export let rowHoverable = true;
12
13
  export let dataSelector = (pageData) => Array.isArray(pageData) ? pageData : [];
13
- $: data = $query.data ? {
14
- ...$query.data,
15
- pages: $query.data.pages.map((page) => dataSelector(page))
16
- } : void 0;
14
+ export let virtualization = {};
15
+ $: enableVirtualization = virtualization.enabled ?? true;
16
+ $: estimatedRowHeight = virtualization.estimatedRowHeight ?? 56;
17
+ $: virtualizationOverscan = virtualization.overscan ?? 8;
18
+ let measuredRowHeight = null;
19
+ $: rowHeight = measuredRowHeight ?? estimatedRowHeight;
20
+ let transformedPages = [];
21
+ let lastPagesRef;
22
+ let lastSelector = dataSelector;
23
+ $: {
24
+ const currentData = $query.data;
25
+ const currentPages = currentData?.pages;
26
+ const selectorChanged = lastSelector !== dataSelector;
27
+ if (!currentPages) {
28
+ transformedPages = [];
29
+ lastPagesRef = void 0;
30
+ lastSelector = dataSelector;
31
+ } else if (currentPages !== lastPagesRef || selectorChanged) {
32
+ transformedPages = currentPages.map((page) => dataSelector(page));
33
+ lastPagesRef = currentPages;
34
+ lastSelector = dataSelector;
35
+ }
36
+ }
37
+ $: flattenedRows = transformedPages.flat();
38
+ $: totalRows = flattenedRows.length;
39
+ $: hasData = totalRows > 0;
40
+ const hasWindow = typeof window !== "undefined";
41
+ let tableContainerElement = null;
42
+ let tableOffsetTop = 0;
43
+ let virtualizerStore = null;
44
+ let virtualizer = null;
45
+ let unsubscribeVirtualizer = null;
46
+ let virtualizationActive = false;
47
+ function updateTableOffset() {
48
+ if (!enableVirtualization || !hasWindow || !tableContainerElement) {
49
+ tableOffsetTop = 0;
50
+ return;
51
+ }
52
+ const rect = tableContainerElement.getBoundingClientRect();
53
+ tableOffsetTop = rect.top + window.scrollY;
54
+ }
55
+ onMount(() => {
56
+ if (!hasWindow) {
57
+ return;
58
+ }
59
+ virtualizerStore = createWindowVirtualizer({
60
+ count: totalRows,
61
+ estimateSize: () => rowHeight,
62
+ overscan: virtualizationOverscan,
63
+ scrollMargin: tableOffsetTop,
64
+ getItemKey: (index) => index,
65
+ enabled: enableVirtualization && totalRows > 0
66
+ });
67
+ unsubscribeVirtualizer = virtualizerStore.subscribe((instance) => {
68
+ virtualizer = instance;
69
+ });
70
+ updateTableOffset();
71
+ return () => {
72
+ unsubscribeVirtualizer?.();
73
+ };
74
+ });
75
+ afterUpdate(() => {
76
+ if (enableVirtualization) {
77
+ updateTableOffset();
78
+ }
79
+ });
80
+ $: if (virtualizer) {
81
+ virtualizer.setOptions({
82
+ count: totalRows,
83
+ estimateSize: () => rowHeight,
84
+ overscan: virtualizationOverscan,
85
+ scrollMargin: tableOffsetTop,
86
+ getItemKey: (index) => index,
87
+ enabled: enableVirtualization && totalRows > 0
88
+ });
89
+ }
90
+ $: virtualizationActive = enableVirtualization && Boolean(virtualizer);
91
+ let virtualItems = [];
92
+ let totalSize = 0;
93
+ let topPadding = 0;
94
+ let bottomPadding = 0;
95
+ let scrollMargin = 0;
96
+ $: {
97
+ const hasRows = totalRows > 0;
98
+ scrollMargin = virtualizationActive ? tableOffsetTop : 0;
99
+ if (virtualizationActive && virtualizer && hasRows) {
100
+ virtualItems = virtualizer.getVirtualItems();
101
+ totalSize = virtualizer.getTotalSize();
102
+ const firstItem = virtualItems[0];
103
+ const lastItem = virtualItems[virtualItems.length - 1];
104
+ topPadding = firstItem ? Math.max(0, firstItem.start - scrollMargin) : 0;
105
+ bottomPadding = lastItem ? Math.max(0, totalSize - (lastItem.end - scrollMargin)) : Math.max(0, totalSize);
106
+ } else {
107
+ virtualItems = [];
108
+ totalSize = 0;
109
+ topPadding = 0;
110
+ bottomPadding = 0;
111
+ }
112
+ }
113
+ onDestroy(() => {
114
+ unsubscribeVirtualizer?.();
115
+ });
116
+ afterUpdate(() => {
117
+ if (!virtualizationActive || !virtualizer || !tableContainerElement) {
118
+ return;
119
+ }
120
+ const rows = Array.from(
121
+ tableContainerElement.querySelectorAll('tbody tr[data-virtual-row="true"]')
122
+ );
123
+ if (!rows.length) {
124
+ return;
125
+ }
126
+ for (const row of rows) {
127
+ virtualizer.measureElement(row);
128
+ }
129
+ if (measuredRowHeight === null) {
130
+ const sampleHeight = rows[0].getBoundingClientRect().height;
131
+ if (sampleHeight > 0) {
132
+ measuredRowHeight = sampleHeight;
133
+ }
134
+ }
135
+ });
17
136
  </script>
18
137
 
19
138
  <div data-testid="title" class="flex h-16 w-full items-center justify-end">
@@ -31,34 +150,62 @@ $: data = $query.data ? {
31
150
  }}
32
151
  />
33
152
  </div>
34
- {#if (data?.pages?.[0]?.length ?? 0) === 0}
153
+ {#if totalRows === 0}
35
154
  <div data-testid="emptyMessage" class="text-center text-gray-900 dark:text-white">
36
155
  {emptyMessage}
37
156
  </div>
38
- {:else if data}
39
- <Table
40
- divClass="cursor-pointer rounded-lg overflow-auto dark:border-none border"
41
- hoverable={rowHoverable}
157
+ {:else if hasData}
158
+ <div
159
+ class="cursor-pointer overflow-x-auto rounded-lg border dark:border-none"
160
+ data-testid="tanstackTableContainer"
161
+ bind:this={tableContainerElement}
42
162
  >
43
- <TableHead data-testid="head">
44
- <slot name="head" />
45
- </TableHead>
46
- <TableBody>
47
- {#each data.pages as page}
48
- {#each page as item}
49
- <TableBodyRow
50
- class="whitespace-nowrap"
51
- data-testid="bodyRow"
52
- on:click={() => {
53
- dispatch('clickRow', { item });
54
- }}
55
- >
56
- <slot name="bodyRow" {item} />
57
- </TableBodyRow>
58
- {/each}
59
- {/each}
60
- </TableBody>
61
- </Table>
163
+ <Table divClass="min-w-full" class="w-full table-fixed" hoverable={rowHoverable}>
164
+ <TableHead data-testid="head">
165
+ <slot name="head" />
166
+ </TableHead>
167
+ <TableBody>
168
+ {#if virtualizationActive && topPadding > 0}
169
+ <tr aria-hidden="true">
170
+ <td colspan="1000" class="border-0 p-0" style={`height:${topPadding}px;`} />
171
+ </tr>
172
+ {/if}
173
+ {#if virtualizationActive}
174
+ {#each virtualItems as virtualItem (virtualItem.key)}
175
+ <TableBodyRow
176
+ class="whitespace-nowrap"
177
+ data-testid="bodyRow"
178
+ data-virtual-row="true"
179
+ on:click={() => {
180
+ dispatch('clickRow', { item: flattenedRows[virtualItem.index] });
181
+ }}
182
+ >
183
+ <slot name="bodyRow" item={flattenedRows[virtualItem.index]} />
184
+ </TableBodyRow>
185
+ {/each}
186
+ {:else}
187
+ {#each transformedPages as page}
188
+ {#each page as item}
189
+ <TableBodyRow
190
+ class="whitespace-nowrap"
191
+ data-testid="bodyRow"
192
+ on:click={() => {
193
+ dispatch('clickRow', { item });
194
+ }}
195
+ >
196
+ <slot name="bodyRow" {item} />
197
+ </TableBodyRow>
198
+ {/each}
199
+ {/each}
200
+ {/if}
201
+ {#if virtualizationActive && bottomPadding > 0}
202
+ <tr aria-hidden="true">
203
+ <td colspan="1000" class="border-0 p-0" style={`height:${bottomPadding}px;`} />
204
+ </tr>
205
+ {/if}
206
+ </TableBody>
207
+ </Table>
208
+ </div>
62
209
  <div class="mt-2 flex justify-center">
63
210
  <Button
64
211
  data-testid="loadMoreButton"
@@ -7,6 +7,11 @@ declare class __sveltets_Render<DataItem, InputData = DataItem[]> {
7
7
  emptyMessage?: string;
8
8
  rowHoverable?: boolean;
9
9
  dataSelector?: ((pageData: InputData) => DataItem[]) | undefined;
10
+ virtualization?: {
11
+ enabled?: boolean;
12
+ estimatedRowHeight?: number;
13
+ overscan?: number;
14
+ } | undefined;
10
15
  };
11
16
  events(): {
12
17
  clickRow: CustomEvent<any>;