1import { createSignal, For } from "solid-js";
2import { buildScopeString, GRANULAR_SCOPES, scopeIdsToString } from "./scope-utils";
3
4interface ScopeSelectorProps {
5 onConfirm: (scopeString: string, scopeIds: string) => void;
6 onCancel: () => void;
7 initialScopes?: Set<string>;
8}
9
10export const ScopeSelector = (props: ScopeSelectorProps) => {
11 const [selectedScopes, setSelectedScopes] = createSignal<Set<string>>(
12 props.initialScopes || new Set(["create", "update", "delete", "blob"]),
13 );
14
15 const isBlobDisabled = () => {
16 const scopes = selectedScopes();
17 return !scopes.has("create") && !scopes.has("update");
18 };
19
20 const toggleScope = (scopeId: string) => {
21 setSelectedScopes((prev) => {
22 const newSet = new Set(prev);
23 if (newSet.has(scopeId)) {
24 newSet.delete(scopeId);
25 if (
26 (scopeId === "create" || scopeId === "update") &&
27 !newSet.has("create") &&
28 !newSet.has("update")
29 ) {
30 newSet.delete("blob");
31 }
32 } else {
33 newSet.add(scopeId);
34 }
35 return newSet;
36 });
37 };
38
39 const handleConfirm = () => {
40 const scopes = selectedScopes();
41 const scopeString = buildScopeString(scopes);
42 const scopeIds = scopeIdsToString(scopes);
43 props.onConfirm(scopeString, scopeIds);
44 };
45
46 return (
47 <div class="flex flex-col gap-y-2">
48 <div class="mb-1 flex items-center gap-2">
49 <button
50 onclick={props.onCancel}
51 class="flex items-center rounded-md p-1 hover:bg-neutral-200 active:bg-neutral-300 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
52 >
53 <span class="iconify lucide--arrow-left"></span>
54 </button>
55 <div class="font-semibold">Select permissions</div>
56 </div>
57 <div class="flex flex-col gap-y-2 px-1">
58 <For each={GRANULAR_SCOPES}>
59 {(scope) => (
60 <div
61 class="flex items-center gap-2"
62 classList={{ "opacity-50": scope.id === "blob" && isBlobDisabled() }}
63 >
64 <input
65 id={`scope-${scope.id}`}
66 type="checkbox"
67 checked={selectedScopes().has(scope.id)}
68 disabled={scope.id === "blob" && isBlobDisabled()}
69 onChange={() => toggleScope(scope.id)}
70 />
71 <label for={`scope-${scope.id}`} class="flex grow items-center gap-2 select-none">
72 <span>{scope.label}</span>
73 </label>
74 </div>
75 )}
76 </For>
77 </div>
78 <button
79 onclick={handleConfirm}
80 class="mt-2 grow rounded-lg border-[0.5px] border-neutral-300 bg-neutral-100 px-3 py-2 hover:bg-neutral-200 active:bg-neutral-300 dark:border-neutral-600 dark:bg-neutral-800 dark:hover:bg-neutral-700 dark:active:bg-neutral-600"
81 >
82 Continue
83 </button>
84 </div>
85 );
86};