@stubber/ui 1.5.0 → 1.6.1

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