forked from
pds.ls/pdsls
atmosphere explorer
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-3">
48 <div class="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 px-1">
58 <For each={GRANULAR_SCOPES}>
59 {(scope) => {
60 const isSelected = () => selectedScopes().has(scope.id);
61 const isDisabled = () => scope.id === "blob" && isBlobDisabled();
62
63 return (
64 <button
65 onclick={() => !isDisabled() && toggleScope(scope.id)}
66 disabled={isDisabled()}
67 class="group flex items-center gap-3 py-1.5"
68 classList={{ "opacity-50": isDisabled() }}
69 >
70 <div
71 class="flex size-5 items-center justify-center rounded border-2"
72 classList={{
73 "bg-blue-500 border-transparent group-hover:bg-blue-600 group-active:bg-blue-400":
74 isSelected() && !isDisabled(),
75 "border-neutral-400 dark:border-neutral-500 group-hover:border-neutral-500 dark:group-hover:border-neutral-400 group-hover:bg-neutral-100 dark:group-hover:bg-neutral-800":
76 !isSelected() && !isDisabled(),
77 "border-neutral-300 dark:border-neutral-600": isDisabled(),
78 }}
79 >
80 {isSelected() && <span class="iconify lucide--check text-sm text-white"></span>}
81 </div>
82 <span>{scope.label}</span>
83 </button>
84 );
85 }}
86 </For>
87 </div>
88 <button
89 onclick={handleConfirm}
90 class="dark:hover:bg-dark-200 dark:active:bg-dark-100 flex w-full items-center justify-center gap-2 rounded-lg border border-neutral-200 px-3 py-2 hover:bg-neutral-100 active:bg-neutral-200 dark:border-neutral-700"
91 >
92 Continue
93 </button>
94 </div>
95 );
96};