Browse Source

add silk sidebar and persistent sheet

Tienson Qin 3 months ago
parent
commit
4960790857

+ 25 - 3
deps/shui/src/logseq/shui/silkhq.cljs

@@ -1,6 +1,6 @@
 (ns logseq.shui.silkhq
 (ns logseq.shui.silkhq
-  (:require [logseq.shui.util :refer [component-wrap] :as util]
-            [goog.object :refer [getValueByKeys] :as gobj]))
+  (:require [goog.object :refer [getValueByKeys] :as gobj]
+            [logseq.shui.util :refer [component-wrap] :as util]))
 
 
 (goog-define NODETEST false)
 (goog-define NODETEST false)
 
 
@@ -120,7 +120,6 @@
 (def page-backdrop (silkhq-wrap "Page.Backdrop"))
 (def page-backdrop (silkhq-wrap "Page.Backdrop"))
 (def page-view (silkhq-wrap "Page.View"))
 (def page-view (silkhq-wrap "Page.View"))
 
 
-
 (def card-sheet (silkhq-wrap "CardSheet.Root"))
 (def card-sheet (silkhq-wrap "CardSheet.Root"))
 (def card-sheet-portal (silkhq-wrap "CardSheet.Portal"))
 (def card-sheet-portal (silkhq-wrap "CardSheet.Portal"))
 (def card-sheet-handle (silkhq-wrap "CardSheet.Handle"))
 (def card-sheet-handle (silkhq-wrap "CardSheet.Handle"))
@@ -131,3 +130,26 @@
 (def card-sheet-outlet (silkhq-wrap "CardSheet.Outlet"))
 (def card-sheet-outlet (silkhq-wrap "CardSheet.Outlet"))
 (def card-sheet-backdrop (silkhq-wrap "CardSheet.Backdrop"))
 (def card-sheet-backdrop (silkhq-wrap "CardSheet.Backdrop"))
 (def card-sheet-view (silkhq-wrap "CardSheet.View"))
 (def card-sheet-view (silkhq-wrap "CardSheet.View"))
+
+(def sidebar-sheet (silkhq-wrap "Sidebar.Root"))
+(def sidebar-sheet-portal (silkhq-wrap "Sidebar.Portal"))
+(def sidebar-sheet-view (silkhq-wrap "Sidebar.View"))
+(def sidebar-sheet-backdrop (silkhq-wrap "Sidebar.Backdrop"))
+(def sidebar-sheet-content (silkhq-wrap "Sidebar.Content"))
+(def sidebar-sheet-trigger (silkhq-wrap "Sidebar.Trigger"))
+(def sidebar-sheet-handle (silkhq-wrap "Sidebar.Handle"))
+(def sidebar-sheet-outlet (silkhq-wrap "Sidebar.Outlet"))
+(def sidebar-sheet-title (silkhq-wrap "Sidebar.Title"))
+(def sidebar-sheet-description (silkhq-wrap "Sidebar.Description"))
+
+(def persistent-sheet (silkhq-wrap "PersistentSheetWithDetent.Root"))
+(def persistent-sheet-portal (silkhq-wrap "PersistentSheetWithDetent.Portal"))
+(def persistent-sheet-view (silkhq-wrap "PersistentSheetWithDetent.View"))
+(def persistent-sheet-content (silkhq-wrap "PersistentSheetWithDetent.Content"))
+(def persistent-sheet-trigger (silkhq-wrap "PersistentSheetWithDetent.Trigger"))
+(def persistent-sheet-handle (silkhq-wrap "PersistentSheetWithDetent.Handle"))
+(def persistent-sheet-retracted-content (silkhq-wrap "PersistentSheetWithDetent.RetractedContent"))
+(def persistent-sheet-expanded-content (silkhq-wrap "PersistentSheetWithDetent.ExpandedContent"))
+(def persistent-sheet-outlet (silkhq-wrap "PersistentSheetWithDetent.Outlet"))
+(def persistent-sheet-title (silkhq-wrap "PersistentSheetWithDetent.Title"))
+(def persistent-sheet-description (silkhq-wrap "PersistentSheetWithDetent.Description"))

+ 2 - 2
package.json

@@ -80,8 +80,8 @@
         "cljs:watch": "clojure -M:cljs watch app workers electron",
         "cljs:watch": "clojure -M:cljs watch app workers electron",
         "cljs:storybook-watch": "clojure -M:cljs watch stories-dev",
         "cljs:storybook-watch": "clojure -M:cljs watch stories-dev",
         "gulp:mobile-watch": "gulp watchMobile",
         "gulp:mobile-watch": "gulp watchMobile",
-        "css:mobile-build": "postcss tailwind.mobile.css -o static/mobile/style.css --verbose --env production",
-        "css:mobile-watch": "cross-env TAILWIND_MODE=watch postcss tailwind.mobile.css -o static/mobile/style.css --verbose --watch",
+        "css:mobile-build": "postcss tailwind.mobile.css -o static/mobile/css/style.css --verbose --env production",
+        "css:mobile-watch": "cross-env TAILWIND_MODE=watch postcss tailwind.mobile.css -o static/mobile/css/style.css --verbose --watch",
         "cljs:mobile-watch": "clojure -M:cljs watch mobile workers --config-merge \"{:output-dir \\\"./static/mobile/js\\\" :asset-path \\\"/static/mobile/js\\\" :release {:asset-path \\\"http://localhost\\\"}}\"",
         "cljs:mobile-watch": "clojure -M:cljs watch mobile workers --config-merge \"{:output-dir \\\"./static/mobile/js\\\" :asset-path \\\"/static/mobile/js\\\" :release {:asset-path \\\"http://localhost\\\"}}\"",
         "cljs:release-mobile": "clojure -M:cljs release mobile workers --config-merge \"{:output-dir \\\"./static/mobile/js\\\" :asset-path \\\"/static/mobile/js\\\" :release {:asset-path \\\"http://localhost\\\"}}\"",
         "cljs:release-mobile": "clojure -M:cljs release mobile workers --config-merge \"{:output-dir \\\"./static/mobile/js\\\" :asset-path \\\"/static/mobile/js\\\" :release {:asset-path \\\"http://localhost\\\"}}\"",
         "cljs:dev-watch": "clojure -M:cljs watch app workers electron mobile",
         "cljs:dev-watch": "clojure -M:cljs watch app workers electron mobile",

+ 85 - 0
packages/ui/src/silkhq/PersistentSheetWithDetent.css

@@ -0,0 +1,85 @@
+.PersistentSheetWithDetent-customBackdrop {
+  /* SELF-LAYOUT */
+  position: fixed;
+  z-index: 1;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+
+  /* APPEARANCE */
+  background-color: black;
+
+  /* INTERACTIVITY */
+  pointer-events: none;
+
+  /* TRANSFORMATION */
+  opacity: 0;
+}
+
+.PersistentSheetWithDetent-themeColorDimmingController {
+  position: fixed;
+  z-index: 1;
+  top: -10px;
+  left: 0;
+  width: 100%;
+  height: 10px;
+  background-color: rgba(63, 165, 225, 1);
+  box-shadow: 0 2px 6px -2px rgb(0, 0, 0, 0.25);
+
+  /* TRANSFORMATION */
+  opacity: 0;
+}
+
+.PersistentSheetWithDetent-view {
+  --retracted-height: 76px;
+
+  /* SELF-LAYOUT */
+  z-index: 2;
+  bottom: -1px; /* Avoid subpixel misalignment in Safari */
+  width: 100%;
+}
+.PersistentSheetWithDetent-view.onDetent2-true {
+  /* SELF-LAYOUT */
+  /* When resting on the detent 2, we switch the side the view is
+  anchored to to the top. It looks better when mobile browsers'
+  UI expand/collapse. */
+  bottom: initial;
+  top: 0;
+}
+
+.PersistentSheetWithDetent-content {
+  /* SELF-LAYOUT */
+  box-sizing: border-box;
+  height: 100%;
+
+  /* APPEARANCE */
+  box-shadow: 0 -2px 6px -2px rgb(0, 0, 0, 0.25);
+  background-color: rgba(63, 165, 225, 1);
+}
+
+.PersistentSheetWithDetent-innerContent {
+  /* INNER-LAYOUT */
+  box-sizing: border-box;
+  width: 100%;
+  height: 100%;
+
+  /* INNER-LAYOUT */
+  position: relative;
+  padding-top: env(safe-area-inset-top, 0px);
+  display: grid;
+}
+
+.PersistentSheetWithDetent-retractedContent {
+  /* SELF-LAYOUT */
+  box-sizing: border-box;
+  position: absolute;
+  top: 0;
+  width: 100%;
+  height: var(--retracted-height);
+}
+
+.PersistentSheetWithDetent-expandedContent {
+  /* SELF-LAYOUT */
+  place-self: stretch;
+}

+ 319 - 0
packages/ui/src/silkhq/PersistentSheetWithDetent.tsx

@@ -0,0 +1,319 @@
+"use client";
+import React, { useState, useRef, useCallback, useEffect, useMemo } from "react";
+import { Sheet, useThemeColorDimmingOverlay } from "@silk-hq/components";
+import "./PersistentSheetWithDetent.css";
+
+// ================================================================================================
+// Utils
+// ================================================================================================
+
+const setRefs = <T,>(...refs: (React.Ref<T> | undefined)[]): ((node: T) => void) => {
+  return (node: T) => {
+    refs.forEach((ref) => {
+      if (typeof ref === "function") {
+        ref(node);
+      } else if (ref) {
+        // @ts-ignore - intentionally breaking the readonly nature for compatibility
+        ref.current = node;
+      }
+    });
+  };
+};
+
+// ================================================================================================
+// Context
+// ================================================================================================
+
+type PersistentSheetWithDetentContextValue = {
+  range: { start: number; end: number };
+  setRange: React.Dispatch<React.SetStateAction<{ start: number; end: number }>>;
+  backdropRef: React.RefObject<HTMLDivElement>;
+  themeColorDimmingControllerRef: React.RefObject<HTMLDivElement>;
+  rectractedContentRef: React.RefObject<HTMLDivElement>;
+  expandedContentRef: React.RefObject<HTMLDivElement>;
+};
+
+const PersistentSheetWithDetentContext =
+  React.createContext<PersistentSheetWithDetentContextValue | null>(null);
+
+const usePersistentSheetWithDetentContext = () => {
+  const context = React.useContext(PersistentSheetWithDetentContext);
+  if (!context) {
+    throw new Error(
+      "usePersistentSheetWithDetentContext must be used within a PersistentSheetWithDetentContextProvider"
+    );
+  }
+  return context;
+};
+
+// ================================================================================================
+// Root
+// ================================================================================================
+
+type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
+type PersistentSheetWithDetentRootProps = Omit<SheetRootProps, "license"> & {
+  license?: SheetRootProps["license"];
+};
+
+const PersistentSheetWithDetentRoot = React.forwardRef<
+  React.ElementRef<typeof Sheet.Root>,
+  PersistentSheetWithDetentRootProps
+>(({ children, ...restProps }, ref) => {
+  const [range, setRange] = useState({ start: 0, end: 0 });
+  const backdropRef = useRef<HTMLDivElement>(null);
+  const themeColorDimmingControllerRef = useRef<HTMLDivElement>(null);
+  const rectractedContentRef = useRef<HTMLDivElement>(null);
+  const expandedContentRef = useRef<HTMLDivElement>(null);
+
+  return (
+    <PersistentSheetWithDetentContext.Provider
+      value={{
+        range,
+        setRange,
+        backdropRef,
+        themeColorDimmingControllerRef,
+        rectractedContentRef,
+        expandedContentRef,
+      }}
+    >
+      <Sheet.Root license="commercial" {...restProps} ref={ref}>
+        <Sheet.Portal>
+          {/* Using a custom backdrop because the real one's features are
+              not needed, and to be able to put it outside of the view */}
+          {range.end > 1 && (
+            <>
+              <Sheet.Outlet
+                className="PersistentSheetWithDetent-customBackdrop"
+                ref={backdropRef}
+              />
+              <div
+                className="PersistentSheetWithDetent-themeColorDimmingController"
+                ref={themeColorDimmingControllerRef}
+              />
+            </>
+          )}
+        </Sheet.Portal>
+        {children}
+      </Sheet.Root>
+    </PersistentSheetWithDetentContext.Provider>
+  );
+});
+PersistentSheetWithDetentRoot.displayName = "PersistentSheetWithDetent.Root";
+
+// ================================================================================================
+// View
+// ================================================================================================
+
+const PersistentSheetWithDetentView = React.forwardRef<
+  React.ElementRef<typeof Sheet.View>,
+  React.ComponentPropsWithoutRef<typeof Sheet.View> & {
+    dimmingColor?: string;
+  }
+>(({ children, className, dimmingColor, ...restProps }, ref) => {
+  const viewRef = useRef<HTMLDivElement>(null);
+  const [inertOutside, setInertOutside] = useState(false);
+  const {
+    range,
+    setRange,
+    backdropRef,
+    themeColorDimmingControllerRef,
+    rectractedContentRef,
+    expandedContentRef,
+  } = usePersistentSheetWithDetentContext();
+
+  const rangeChangeHandler: NonNullable<
+    React.ComponentProps<typeof Sheet.View>["onTravelRangeChange"]
+  > = useCallback(
+    (newRange) => {
+      setRange(newRange);
+      setInertOutside(newRange.start === 2 && newRange.end === 2);
+    },
+    [setRange]
+  );
+
+  //
+  // Height setter
+
+  // We set the height only when the sheet is resting on a
+  // detent.
+
+  useEffect(() => {
+    const updateHeight = () => {
+      if (viewRef.current && range.start === range.end) {
+        const height = window.innerHeight;
+        viewRef.current.style.height = `${height + 1}px`;
+      }
+    };
+
+    if (range.start === range.end) {
+      updateHeight();
+    }
+
+    visualViewport?.addEventListener("resize", updateHeight);
+
+    return () => {
+      visualViewport?.removeEventListener("resize", updateHeight);
+    };
+  }, [range.start, range.end]);
+
+  //
+  // Travel handler
+
+  // We use a travel handler instead of travel animations because
+  // it is not (yet) to define animation based on the progress
+  // during a specific range.
+
+  const { setDimmingOverlayOpacity } = useThemeColorDimmingOverlay({
+    elementRef: themeColorDimmingControllerRef,
+    dimmingColor: dimmingColor ?? "rgba(63, 165, 225, 1)",
+  });
+
+  const travelHandler: NonNullable<React.ComponentProps<typeof Sheet.View>["onTravel"]> =
+    useCallback(
+      ({ progress, range, progressAtDetents }) => {
+        if (!progressAtDetents) return;
+
+        if (range.end > 1) {
+          const normalisedProgress = (progress - progressAtDetents[1]) / (1 - progressAtDetents[1]);
+
+          setDimmingOverlayOpacity(normalisedProgress);
+
+          rectractedContentRef.current?.style.setProperty(
+            "opacity",
+            (1 - normalisedProgress) as unknown as string
+          );
+          backdropRef.current?.style.setProperty(
+            "opacity",
+            (normalisedProgress * 0.25) as unknown as string
+          );
+          expandedContentRef.current?.style.setProperty(
+            "opacity",
+            normalisedProgress as unknown as string
+          );
+        }
+      },
+      [setDimmingOverlayOpacity]
+    );
+
+  //
+  // Return
+
+  const onDetent2 = useMemo(() => range.start === 2 && range.end === 2, [range.start, range.end]);
+
+  return (
+    <Sheet.View
+      ref={setRefs(viewRef, ref)}
+      className={`PersistentSheetWithDetent-view onDetent2-${onDetent2} ${className ?? ""}`.trim()}
+      detents="max(env(safe-area-inset-bottom, 0px) - 10px + var(--retracted-height), var(--retracted-height))"
+      swipeOvershoot={false}
+      swipeDismissal={false}
+      onTravelRangeChange={rangeChangeHandler}
+      inertOutside={inertOutside}
+      onClickOutside={{ dismiss: range.end === 2 }}
+      onTravel={travelHandler}
+      nativeEdgeSwipePrevention={range.end !== 1}
+      {...restProps}
+    >
+      {children}
+    </Sheet.View>
+  );
+});
+PersistentSheetWithDetentView.displayName = "PersistentSheetWithDetent.View";
+
+// ================================================================================================
+// Content
+// ================================================================================================
+
+const PersistentSheetWithDetentContent = React.forwardRef<
+  React.ElementRef<typeof Sheet.Content>,
+  React.ComponentPropsWithoutRef<typeof Sheet.Content>
+>(({ children, className, ...restProps }, ref) => {
+  return (
+    <Sheet.Content
+      className={`PersistentSheetWithDetent-content ${className ?? ""}`.trim()}
+      {...restProps}
+      ref={ref}
+    >
+      <Sheet.SpecialWrapper.Root>
+        <Sheet.SpecialWrapper.Content>
+          <div className="PersistentSheetWithDetent-innerContent">{children}</div>
+        </Sheet.SpecialWrapper.Content>
+      </Sheet.SpecialWrapper.Root>
+    </Sheet.Content>
+  );
+});
+PersistentSheetWithDetentContent.displayName = "PersistentSheetWithDetent.Content";
+
+// ================================================================================================
+// Retracted Content
+// ================================================================================================
+
+const PersistentSheetWithDetentRetractedContent = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLDivElement>
+>(({ children, className, ...restProps }, ref) => {
+  const { range, rectractedContentRef } = usePersistentSheetWithDetentContext();
+
+  if (range.start >= 2) return null;
+
+  return (
+    <div
+      className={`PersistentSheetWithDetent-retractedContent ${className ?? ""}`.trim()}
+      ref={setRefs(rectractedContentRef, ref)}
+      {...restProps}
+    >
+      {children}
+    </div>
+  );
+});
+PersistentSheetWithDetentRetractedContent.displayName =
+  "PersistentSheetWithDetent.RetractedContent";
+
+// ================================================================================================
+// Expanded Content
+// ================================================================================================
+
+const PersistentSheetWithDetentExpandedContent = React.forwardRef<
+  HTMLDivElement,
+  React.HTMLAttributes<HTMLDivElement>
+>(({ children, className, ...restProps }, ref) => {
+  const { range, expandedContentRef } = usePersistentSheetWithDetentContext();
+
+  if (range.end <= 1) return null;
+
+  return (
+    <div
+      className={`PersistentSheetWithDetent-expandedContent ${className ?? ""}`.trim()}
+      ref={setRefs(expandedContentRef, ref)}
+      {...restProps}
+    >
+      {children}
+    </div>
+  );
+});
+PersistentSheetWithDetentExpandedContent.displayName = "PersistentSheetWithDetent.ExpandedContent";
+
+// ================================================================================================
+// Unchanged components
+// ================================================================================================
+
+const PersistentSheetWithDetentPortal = Sheet.Portal;
+const PersistentSheetWithDetentTrigger = Sheet.Trigger;
+const PersistentSheetWithDetentHandle = Sheet.Handle;
+const PersistentSheetWithOutlet = Sheet.Outlet;
+const PersistentSheetWithTitle = Sheet.Title;
+const PersistentSheetWithDescription = Sheet.Description;
+
+export const PersistentSheetWithDetent = {
+  Root: PersistentSheetWithDetentRoot,
+  Portal: PersistentSheetWithDetentPortal,
+  View: PersistentSheetWithDetentView,
+  Content: PersistentSheetWithDetentContent,
+  Trigger: PersistentSheetWithDetentTrigger,
+  Handle: PersistentSheetWithDetentHandle,
+  RetractedContent: PersistentSheetWithDetentRetractedContent,
+  ExpandedContent: PersistentSheetWithDetentExpandedContent,
+  Outlet: PersistentSheetWithOutlet,
+  Title: PersistentSheetWithTitle,
+  Description: PersistentSheetWithDescription,
+};

+ 16 - 0
packages/ui/src/silkhq/Sidebar.css

@@ -0,0 +1,16 @@
+.Sidebar-view {
+  /* SELF-LAYOUT */
+  z-index: 1;
+  /* Adding 60px to make it fully visible below iOS Safari's bottom UI */
+  height: calc(var(--silk-100-lvh-dvh-pct) + 60px);
+}
+
+.Sidebar-content {
+  /* SELF-LAYOUT */
+  box-sizing: border-box;
+  width: min(90vw, 325px);
+
+  /* APPEARANCE */
+  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
+  background-color: white;
+}

+ 109 - 0
packages/ui/src/silkhq/Sidebar.tsx

@@ -0,0 +1,109 @@
+"use client";
+import React from "react";
+import { Sheet, VisuallyHidden } from "@silk-hq/components";
+import "./Sidebar.css";
+
+// ================================================================================================
+// Root
+// ================================================================================================
+
+type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
+type SidebarRootProps = Omit<SheetRootProps, "license"> & {
+  license?: SheetRootProps["license"];
+};
+
+const SidebarRoot = React.forwardRef<React.ElementRef<typeof Sheet.Root>, SidebarRootProps>(
+  ({ children, ...restProps }, ref) => {
+    return (
+      <Sheet.Root license="commercial" sheetRole="dialog" {...restProps} ref={ref}>
+        {children}
+      </Sheet.Root>
+    );
+  }
+);
+SidebarRoot.displayName = "Sidebar.Root";
+
+// ================================================================================================
+// View
+// ================================================================================================
+
+const SidebarView = React.forwardRef<
+  React.ElementRef<typeof Sheet.View>,
+  React.ComponentPropsWithoutRef<typeof Sheet.View>
+>(({ children, className, ...restProps }, ref) => {
+  return (
+    <Sheet.View
+      className={`Sidebar-view ${className ?? ""}`.trim()}
+      contentPlacement="left"
+      swipeOvershoot={false}
+      nativeEdgeSwipePrevention={true}
+      {...restProps}
+      ref={ref}
+    >
+      {children}
+    </Sheet.View>
+  );
+});
+SidebarView.displayName = "Sidebar.View";
+
+// ================================================================================================
+// Backdrop
+// ================================================================================================
+
+const SidebarBackdrop = React.forwardRef<
+  React.ElementRef<typeof Sheet.Backdrop>,
+  React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
+>(({ className, ...restProps }, ref) => {
+  return (
+    <Sheet.Backdrop
+      className={`Sidebar-backdrop ${className ?? ""}`.trim()}
+      {...restProps}
+      ref={ref}
+    />
+  );
+});
+SidebarBackdrop.displayName = "Sidebar.Backdrop";
+
+// ================================================================================================
+// Content
+// ================================================================================================
+
+const SidebarContent = React.forwardRef<
+  React.ElementRef<typeof Sheet.Content>,
+  React.ComponentPropsWithoutRef<typeof Sheet.Content>
+>(({ children, className, ...restProps }, ref) => {
+  return (
+    <Sheet.Content className={`Sidebar-content ${className ?? ""}`.trim()} {...restProps} ref={ref}>
+      <VisuallyHidden.Root>
+        <Sheet.Title>Sidebar Example</Sheet.Title>
+        <Sheet.Trigger action="dismiss">Close Sidebar example</Sheet.Trigger>
+      </VisuallyHidden.Root>
+      {children}
+    </Sheet.Content>
+  );
+});
+SidebarContent.displayName = "Sidebar.Content";
+
+// ================================================================================================
+// Unchanged Components
+// ================================================================================================
+
+const SidebarPortal = Sheet.Portal;
+const SidebarTrigger = Sheet.Trigger;
+const SidebarHandle = Sheet.Handle;
+const SidebarOutlet = Sheet.Outlet;
+const SidebarTitle = Sheet.Title;
+const SidebarDescription = Sheet.Description;
+
+export const Sidebar = {
+  Root: SidebarRoot,
+  Portal: SidebarPortal,
+  View: SidebarView,
+  Backdrop: SidebarBackdrop,
+  Content: SidebarContent,
+  Trigger: SidebarTrigger,
+  Handle: SidebarHandle,
+  Outlet: SidebarOutlet,
+  Title: SidebarTitle,
+  Description: SidebarDescription,
+};

+ 4 - 2
packages/ui/src/silkhq/silkhq.ts

@@ -8,6 +8,8 @@ import { ParallaxPage, ParallaxPageStack } from './ParallaxPage'
 import { Toast } from './Toast'
 import { Toast } from './Toast'
 import { Card } from './Card'
 import { Card } from './Card'
 import { Page } from './Page'
 import { Page } from './Page'
+import { Sidebar } from './Sidebar'
+import { PersistentSheetWithDetent } from './PersistentSheetWithDetent'
 
 
 declare global {
 declare global {
   var LSSilkhq: any
   var LSSilkhq: any
@@ -19,9 +21,9 @@ const silkhq = {
   SheetWithStacking, SheetWithDetent,
   SheetWithStacking, SheetWithDetent,
   SheetWithStackingStack,
   SheetWithStackingStack,
   ParallaxPage, ParallaxPageStack,
   ParallaxPage, ParallaxPageStack,
-  Toast, CardSheet: Card, Page
+  Toast, CardSheet: Card, Page, Sidebar, PersistentSheetWithDetent
 }
 }
 
 
 window.LSSilkhq = silkhq
 window.LSSilkhq = silkhq
 
 
-export default silkhq
+export default silkhq