@stubber/ui 1.6.1 → 1.6.2
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/dist/components/OrgSwitcher.svelte +143 -117
- package/package.json +1 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import { createEventDispatcher } from "svelte";
|
|
2
|
+
import { createEventDispatcher, tick } from "svelte";
|
|
3
3
|
import { slide } from "svelte/transition";
|
|
4
4
|
import { quintOut } from "svelte/easing";
|
|
5
|
-
import
|
|
5
|
+
import * as Popover from "@stubber/ui/popover";
|
|
6
|
+
import * as Command from "@stubber/ui/command";
|
|
7
|
+
import * as Tooltip from "@stubber/ui/tooltip";
|
|
8
|
+
import { Button } from "@stubber/ui/button";
|
|
9
|
+
import { toast } from "svelte-sonner";
|
|
6
10
|
|
|
7
11
|
export let orgs = [];
|
|
8
12
|
export let selectedorguuid = undefined;
|
|
@@ -18,66 +22,76 @@
|
|
|
18
22
|
success: "success-500",
|
|
19
23
|
};
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
// Combobox state
|
|
26
|
+
let open = false;
|
|
27
|
+
let searchValue = "";
|
|
22
28
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
function toggleMenu() {
|
|
29
|
-
if (orgs.length > 0) {
|
|
30
|
-
state.isMenu = !state.isMenu;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
29
|
+
// Filter orgs based on search
|
|
30
|
+
$: filteredOrgs = orgs.filter((org) =>
|
|
31
|
+
org.label.toLowerCase().includes(searchValue.toLowerCase())
|
|
32
|
+
);
|
|
33
33
|
|
|
34
34
|
function selectOrg(orguuid) {
|
|
35
35
|
dispatch("selectOrg", orguuid);
|
|
36
|
-
selectedorguuid = orguuid;
|
|
37
|
-
state.isMenu = false;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
function closeAndFocusTrigger(triggerId) {
|
|
39
|
+
open = false;
|
|
40
|
+
searchValue = "";
|
|
41
|
+
tick().then(() => {
|
|
42
|
+
document.getElementById(triggerId)?.focus();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function copyToClipboard(uuid, event) {
|
|
47
|
+
if (event) {
|
|
48
|
+
event.stopPropagation();
|
|
49
|
+
event.preventDefault();
|
|
44
50
|
}
|
|
51
|
+
navigator.clipboard.writeText(uuid);
|
|
52
|
+
toast.success("UUID copied to clipboard!");
|
|
45
53
|
}
|
|
46
54
|
</script>
|
|
47
55
|
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
<Popover.Root bind:open let:ids>
|
|
57
|
+
<Popover.Trigger asChild let:builder>
|
|
58
|
+
<Button
|
|
59
|
+
builders={[builder]}
|
|
60
|
+
id={ids.trigger}
|
|
61
|
+
variant="outline"
|
|
62
|
+
role="combobox"
|
|
63
|
+
aria-expanded={open}
|
|
64
|
+
class="w-full sm:w-[280px] pl-3 pr-3 py-2 h-auto border-surface-100 transition-colors group justify-start"
|
|
65
|
+
>
|
|
66
|
+
<div class="flex items-center w-full">
|
|
67
|
+
<div class="flex items-center justify-center mr-3">
|
|
58
68
|
{#if selectedOrg?.filesrc && !loading}
|
|
59
69
|
<img
|
|
60
|
-
class="h-[25px]
|
|
70
|
+
class="h-[25px] w-[25px] object-cover rounded-full"
|
|
61
71
|
src={selectedOrg.filesrc}
|
|
62
72
|
alt={selectedOrg.label}
|
|
63
73
|
/>
|
|
74
|
+
{:else if selectedOrg?.placeholderColor}
|
|
75
|
+
<div
|
|
76
|
+
class="h-[25px] w-[25px] rounded-full"
|
|
77
|
+
style="background-color: {selectedOrg.placeholderColor}"
|
|
78
|
+
/>
|
|
64
79
|
{:else}
|
|
65
|
-
<div class="h-[25px]
|
|
80
|
+
<div class="h-[25px] w-[25px] rounded-full bg-surface-100" />
|
|
66
81
|
{/if}
|
|
67
82
|
</div>
|
|
83
|
+
|
|
68
84
|
{#if loading}
|
|
69
|
-
<span class="
|
|
85
|
+
<span class="h-[25px] w-40 bg-surface-100 rounded-md" />
|
|
70
86
|
{:else}
|
|
71
|
-
<div class="
|
|
87
|
+
<div class="w-full text-left truncate">
|
|
72
88
|
<p class="text-label text-surface-900 truncate">
|
|
73
|
-
{selectedOrg?.label ?? "No
|
|
89
|
+
{selectedOrg?.label ?? "No Org"}
|
|
74
90
|
</p>
|
|
75
91
|
{#if selectedOrg?.alert}
|
|
76
92
|
<div class="flex items-center space-x-1">
|
|
77
93
|
<span
|
|
78
|
-
class="h-[6px]
|
|
79
|
-
selectedOrg.alert?.type
|
|
80
|
-
]} rounded-full"
|
|
94
|
+
class="h-[6px] w-[6px] bg-{alertColorMap[selectedOrg.alert?.type]} rounded-full"
|
|
81
95
|
/>
|
|
82
96
|
<p class="text-small italic text-{alertColorMap[selectedOrg.alert?.type]}">
|
|
83
97
|
{selectedOrg.alert?.message}
|
|
@@ -86,71 +100,82 @@
|
|
|
86
100
|
{/if}
|
|
87
101
|
</div>
|
|
88
102
|
{/if}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
|
|
104
|
+
{#if selectedOrg}
|
|
105
|
+
<Tooltip.Root openDelay={0} disableHoverableContent={true}>
|
|
106
|
+
<Tooltip.Trigger>
|
|
107
|
+
<div
|
|
108
|
+
class="opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity ml-2 mr-2"
|
|
109
|
+
>
|
|
110
|
+
<i
|
|
111
|
+
role="button"
|
|
112
|
+
tabindex="0"
|
|
113
|
+
on:click={(e) => {
|
|
114
|
+
e.stopPropagation();
|
|
115
|
+
copyToClipboard(selectedOrg.orguuid, e);
|
|
116
|
+
}}
|
|
117
|
+
on:keydown={(e) => {
|
|
118
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
119
|
+
e.stopPropagation();
|
|
120
|
+
copyToClipboard(selectedOrg.orguuid, e);
|
|
121
|
+
}
|
|
122
|
+
}}
|
|
123
|
+
class="far fa-copy text-surface-400 cursor-pointer"
|
|
124
|
+
/>
|
|
125
|
+
</div>
|
|
126
|
+
</Tooltip.Trigger>
|
|
127
|
+
<Tooltip.Content>Copy UUID</Tooltip.Content>
|
|
128
|
+
</Tooltip.Root>
|
|
95
129
|
{/if}
|
|
96
|
-
<div
|
|
97
|
-
class="{loading
|
|
98
|
-
? 'block sm:hidden'
|
|
99
|
-
: ''} ml-auto h-10 aspect-square flex items-center justify-center"
|
|
100
|
-
>
|
|
101
|
-
<i class="text-surface-900 fa far fa-chevron-down {state.isMenu ? 'rotate down' : ''}" />
|
|
102
|
-
</div>
|
|
103
|
-
</button>
|
|
104
|
-
{:else}
|
|
105
|
-
<div
|
|
106
|
-
class="h-10 w-[230px] sm:w-[280px] flex items-center border-x border-t rounded-x-lg rounded-t-lg"
|
|
107
|
-
>
|
|
108
|
-
<div class="h-full aspect-square flex items-center justify-center">
|
|
109
|
-
<i class="text-surface-900 fa far fa-magnifying-glass" />
|
|
110
|
-
</div>
|
|
111
|
-
<input
|
|
112
|
-
type="text"
|
|
113
|
-
autofocus
|
|
114
|
-
class="w-full pr-3 text-surface-900 text-label focus:outline-none"
|
|
115
|
-
placeholder="Search orgs"
|
|
116
|
-
bind:value={filterText}
|
|
117
|
-
/>
|
|
118
130
|
</div>
|
|
119
|
-
|
|
131
|
+
</Button>
|
|
132
|
+
</Popover.Trigger>
|
|
120
133
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
<Popover.Content
|
|
135
|
+
class="w-full sm:w-[280px] p-0"
|
|
136
|
+
transition={slide}
|
|
137
|
+
transitionConfig={{ duration: 300, easing: quintOut, axis: "y" }}
|
|
138
|
+
>
|
|
139
|
+
<Command.Root shouldFilter={false}>
|
|
140
|
+
<Command.Input placeholder="Search organizations..." bind:value={searchValue} />
|
|
141
|
+
<Command.Empty>No organizations found.</Command.Empty>
|
|
142
|
+
<Command.Group class="max-h-80 overflow-y-auto">
|
|
143
|
+
{#each filteredOrgs as org}
|
|
144
|
+
<div class="relative group">
|
|
145
|
+
<Command.Item
|
|
146
|
+
value={org.orguuid}
|
|
147
|
+
onSelect={() => {
|
|
148
|
+
selectOrg(org.orguuid);
|
|
149
|
+
closeAndFocusTrigger(ids.trigger);
|
|
150
|
+
}}
|
|
151
|
+
class="pr-12"
|
|
152
|
+
>
|
|
153
|
+
<div class="flex items-center w-full">
|
|
154
|
+
<div class="flex items-center justify-center mr-3">
|
|
134
155
|
{#if org.filesrc}
|
|
135
156
|
<img
|
|
136
|
-
class="h-[25px]
|
|
157
|
+
class="h-[25px] w-[25px] object-cover rounded-full"
|
|
137
158
|
src={org.filesrc}
|
|
138
159
|
alt={org.label}
|
|
139
160
|
/>
|
|
161
|
+
{:else if org.placeholderColor}
|
|
162
|
+
<div
|
|
163
|
+
class="h-[25px] w-[25px] rounded-full"
|
|
164
|
+
style="background-color: {org.placeholderColor}"
|
|
165
|
+
/>
|
|
140
166
|
{:else}
|
|
141
|
-
<div class="h-[25px]
|
|
167
|
+
<div class="h-[25px] w-[25px] rounded-full bg-surface-100" />
|
|
142
168
|
{/if}
|
|
143
169
|
</div>
|
|
144
|
-
|
|
145
|
-
|
|
170
|
+
|
|
171
|
+
<div class="flex-1 text-left">
|
|
172
|
+
<p class="text-label text-surface-900">
|
|
146
173
|
{org.label}
|
|
147
174
|
</p>
|
|
148
175
|
{#if org.alert}
|
|
149
176
|
<div class="flex items-center space-x-1">
|
|
150
177
|
<span
|
|
151
|
-
class="h-[6px]
|
|
152
|
-
org.alert.type
|
|
153
|
-
]} rounded-full"
|
|
178
|
+
class="h-[6px] w-[6px] bg-{alertColorMap[org.alert.type]} rounded-full"
|
|
154
179
|
/>
|
|
155
180
|
<p class="text-small italic text-{alertColorMap[org.alert.type]}">
|
|
156
181
|
{org.alert?.message}
|
|
@@ -158,36 +183,37 @@
|
|
|
158
183
|
</div>
|
|
159
184
|
{/if}
|
|
160
185
|
</div>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
<i class="text-success-500 fa far fa-check" />
|
|
164
|
-
</div>
|
|
165
|
-
{/if}
|
|
166
|
-
</button>
|
|
167
|
-
{/if}
|
|
168
|
-
{/each}
|
|
169
|
-
</div>
|
|
170
|
-
|
|
171
|
-
{#if createOrgLink}
|
|
172
|
-
<a
|
|
173
|
-
href={createOrgLink}
|
|
174
|
-
class="h-10 w-full pl-3.5 pr-3 flex items-center bg-white text-primary-500 rounded-b-lg hover:bg-primary-50 focus:bg-primary-50 focus:outline-none hover:text-primary-400"
|
|
175
|
-
>
|
|
176
|
-
<i class="far fa fa-plus mr-2" />
|
|
177
|
-
<span class="text-placeholder">Create org</span>
|
|
178
|
-
</a>
|
|
179
|
-
{/if}
|
|
180
|
-
</div>
|
|
181
|
-
{/if}
|
|
182
|
-
</div>
|
|
183
|
-
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</Command.Item>
|
|
184
188
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
+
<!-- Copy button positioned absolutely -->
|
|
190
|
+
<div class="absolute right-2 top-1/2 -translate-y-1/2">
|
|
191
|
+
<Button
|
|
192
|
+
variant="ghost"
|
|
193
|
+
size="icon"
|
|
194
|
+
class="h-8 w-8 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity"
|
|
195
|
+
on:click={(e) => {
|
|
196
|
+
e.stopPropagation();
|
|
197
|
+
copyToClipboard(org.orguuid, e);
|
|
198
|
+
}}
|
|
199
|
+
>
|
|
200
|
+
<i class="far fa-copy text-surface-400" />
|
|
201
|
+
</Button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
{/each}
|
|
205
|
+
</Command.Group>
|
|
189
206
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
207
|
+
{#if createOrgLink}
|
|
208
|
+
<Command.Separator />
|
|
209
|
+
<a
|
|
210
|
+
href={createOrgLink}
|
|
211
|
+
class="flex items-center px-8 py-2 text-sm text-primary-500 hover:bg-accent rounded-sm"
|
|
212
|
+
>
|
|
213
|
+
<i class="far fa-plus mr-2" />
|
|
214
|
+
<span>Create org</span>
|
|
215
|
+
</a>
|
|
216
|
+
{/if}
|
|
217
|
+
</Command.Root>
|
|
218
|
+
</Popover.Content>
|
|
219
|
+
</Popover.Root>
|