Panel.res

 1open Preact
 2
 3// add a halfway point snap only on mobile
 4let snapPoints = Signal.computed(() => {
 5  Window.isMobile->Signal.get ? [0., 0.06, 0.5, 1.0] : [0., 0.06, 1.0]
 6})
 7
 8// convenience to know if sheet is minized
 9let isMinimized = Signal.make(false)
10
11// control sheet height on resize
12let onResize = (ref: PreactDOM.Ref.currentDomRef) =>
13  () => {
14    Log.debug(
15      "resize",
16      ~kv={
17        "mobile": Window.isMobile->Signal.get,
18        "snap": Sheet.currentSnap(ref),
19      },
20    )
21
22    // this function is a hack, but basically prevents a transition from mobile->desktop that leaves
23    // the sheet in a half-height state before new snap points take effect
24    if !(Window.isMobile->Signal.get) && Sheet.currentSnap(ref) == 2 {
25      Sheet.snapTo(ref, 3)
26    }
27
28    // prevents a minimized sheet in desktop from disappearing on switch to mobile
29    if Window.isMobile->Signal.get && isMinimized->Signal.get {
30      Sheet.snapTo(ref, 1)
31    }
32  }
33
34@jsx.component
35let make = (
36  ~children: Jsx.element,
37  ~onMinimize: option<unit => unit>=?,
38  ~onOpen: option<unit => unit>=?,
39) => {
40  let sheetRef = useRef(Nullable.null)
41  Signal.effect(onResize(sheetRef))
42
43  <Sheet
44    ref={PreactDOM.Ref.domRef(sheetRef)}
45    isOpen={true}
46    onClose={() => Console.debug("closed")}
47    snapPoints={snapPoints->Signal.get}
48    onSnap={(~index) => {
49      if index == 1 {
50        isMinimized->Signal.set(true)
51        switch onMinimize {
52        | Some(fn) => fn()
53        | None => ()
54        }
55      } else {
56        isMinimized->Signal.set(false)
57        switch onOpen {
58        | Some(fn) => fn()
59        | None => ()
60        }
61      }
62      Log.debug("sheet position", ~kv={"from": Sheet.currentSnap(sheetRef), "to": index})
63    }}
64    initialSnap={2}
65    disableDismiss={true}
66    detent={#full}
67  >
68    <Sheet.Container>
69      {Window.isMobile->Signal.get
70        ? <Sheet.Header />
71        : <Sheet.Header>
72            <div className="flex flex-row justify-end w-full px-4">
73              <div
74                className="hover:bg-bg-2 p-2 rounded-sm"
75                onClick={_evt =>
76                  if isMinimized->Signal.get {
77                    Sheet.snapTo(sheetRef, 2)
78                  } else {
79                    Sheet.snapTo(sheetRef, 1)
80                  }}
81              >
82                <Icons.ChevronDown
83                  className={cls("text-ui-2", dict{"rotate-180": isMinimized->Signal.get})}
84                />
85              </div>
86            </div>
87          </Sheet.Header>}
88      <Sheet.Content> {children} </Sheet.Content>
89    </Sheet.Container>
90  </Sheet>
91}