mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 22:25:01 +00:00
enhance(mobile): add silkhq basic components
This commit is contained in:
@@ -7,7 +7,8 @@
|
||||
"watch:ui:examples": "parcel serve ./examples/index.html",
|
||||
"build:ui:only": "parcel build --target ui",
|
||||
"build:ionic:only": "parcel build --target ionic",
|
||||
"build:ui": "rm -rf .parcel-cache && yarn build:ui:only && yarn build:ionic:only",
|
||||
"build:silkhq:only": "parcel build --target silkhq",
|
||||
"build:ui": "rm -rf .parcel-cache && yarn build:ui:only && yarn build:ionic:only && yarn build:silkhq:only",
|
||||
"watch:storybook": "storybook dev -p 6006",
|
||||
"postinstall": "yarn build:ui"
|
||||
},
|
||||
@@ -34,6 +35,7 @@
|
||||
"@radix-ui/react-toggle-group": "^1.1.7",
|
||||
"@radix-ui/react-tooltip": "^1.2.4",
|
||||
"@ionic/react": "8.5.7",
|
||||
"@silk-hq/components": "^0.9.10",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.0.0",
|
||||
"cmdk": "^0.2.0",
|
||||
@@ -107,6 +109,14 @@
|
||||
"react": false,
|
||||
"react-dom": false
|
||||
}
|
||||
},
|
||||
"silkhq": {
|
||||
"source": "src/silkhq/silkhq.ts",
|
||||
"outputFormat": "global",
|
||||
"includeNodeModules": {
|
||||
"react": false,
|
||||
"react-dom": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
34
packages/ui/src/silkhq/BottomSheet.css
Normal file
34
packages/ui/src/silkhq/BottomSheet.css
Normal file
@@ -0,0 +1,34 @@
|
||||
.BottomSheet-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);
|
||||
}
|
||||
|
||||
.BottomSheet-content {
|
||||
/* SELF-LAYOUT */
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.BottomSheet-bleedingBackground {
|
||||
/* APPEARANCE */
|
||||
border-radius: 24px;
|
||||
background-color: white;
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.BottomSheet-handle {
|
||||
/* SELF-LAYOUT */
|
||||
width: 50px;
|
||||
height: 6px;
|
||||
|
||||
/* APPEARANCE */
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
background-color: rgb(209, 213, 219);
|
||||
|
||||
/* INTERACTIVITY */
|
||||
cursor: pointer;
|
||||
}
|
||||
126
packages/ui/src/silkhq/BottomSheet.tsx
Normal file
126
packages/ui/src/silkhq/BottomSheet.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
import React from "react";
|
||||
import { Sheet } from "@silk-hq/components";
|
||||
import "./BottomSheet.css";
|
||||
|
||||
// ================================================================================================
|
||||
// Root
|
||||
// ================================================================================================
|
||||
|
||||
type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
|
||||
type BottomSheetRootProps = Omit<SheetRootProps, "license"> & {
|
||||
license?: SheetRootProps["license"];
|
||||
};
|
||||
|
||||
const BottomSheetRoot = React.forwardRef<React.ElementRef<typeof Sheet.Root>, BottomSheetRootProps>(
|
||||
({ children, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Root license="commercial" {...restProps} ref={ref}>
|
||||
{children}
|
||||
</Sheet.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
BottomSheetRoot.displayName = "BottomSheet.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// View
|
||||
// ================================================================================================
|
||||
|
||||
const BottomSheetView = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.View>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.View>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.View
|
||||
className={`BottomSheet-view ${className ?? ""}`.trim()}
|
||||
nativeEdgeSwipePrevention={true}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Sheet.View>
|
||||
);
|
||||
});
|
||||
BottomSheetView.displayName = "BottomSheet.View";
|
||||
|
||||
// ================================================================================================
|
||||
// Backdrop
|
||||
// ================================================================================================
|
||||
|
||||
const BottomSheetBackdrop = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Backdrop>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
|
||||
>(({ className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Backdrop
|
||||
className={`BottomSheet-backdrop ${className ?? ""}`.trim()}
|
||||
themeColorDimming="auto"
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
BottomSheetBackdrop.displayName = "BottomSheet.Backdrop";
|
||||
|
||||
// ================================================================================================
|
||||
// Content
|
||||
// ================================================================================================
|
||||
|
||||
const BottomSheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Content>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Content
|
||||
className={`BottomSheet-content ${className ?? ""}`.trim()}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
<Sheet.BleedingBackground className="BottomSheet-bleedingBackground" />
|
||||
{children}
|
||||
</Sheet.Content>
|
||||
);
|
||||
});
|
||||
BottomSheetContent.displayName = "BottomSheet.Content";
|
||||
|
||||
// ================================================================================================
|
||||
// Handle
|
||||
// ================================================================================================
|
||||
|
||||
const BottomSheetHandle = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Handle>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Handle>
|
||||
>(({ className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Handle
|
||||
className={`BottomSheet-handle ${className ?? ""}`.trim()}
|
||||
action="dismiss"
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
BottomSheetHandle.displayName = "BottomSheet.Handle";
|
||||
|
||||
// ================================================================================================
|
||||
// Unchanged Components
|
||||
// ================================================================================================
|
||||
|
||||
const BottomSheetPortal = Sheet.Portal;
|
||||
const BottomSheetTrigger = Sheet.Trigger;
|
||||
const BottomSheetOutlet = Sheet.Outlet;
|
||||
const BottomSheetTitle = Sheet.Title;
|
||||
const BottomSheetDescription = Sheet.Description;
|
||||
|
||||
export const BottomSheet = {
|
||||
Root: BottomSheetRoot,
|
||||
Portal: BottomSheetPortal,
|
||||
View: BottomSheetView,
|
||||
Backdrop: BottomSheetBackdrop,
|
||||
Content: BottomSheetContent,
|
||||
Trigger: BottomSheetTrigger,
|
||||
Handle: BottomSheetHandle,
|
||||
Outlet: BottomSheetOutlet,
|
||||
Title: BottomSheetTitle,
|
||||
Description: BottomSheetDescription,
|
||||
};
|
||||
63
packages/ui/src/silkhq/SheetWithDepth.css
Normal file
63
packages/ui/src/silkhq/SheetWithDepth.css
Normal file
@@ -0,0 +1,63 @@
|
||||
/* Stack Scenery */
|
||||
|
||||
.SheetWithDepth-stackSceneryBackground {
|
||||
/* SELF-LAYOUT */
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
/* APPEARANCE */
|
||||
background-color: black;
|
||||
opacity: 0;
|
||||
|
||||
will-change: opacity;
|
||||
}
|
||||
.SheetWithDepth-stackSceneryBackground.nativePageScrollReplaced-true {
|
||||
/* APPEARANCE */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.SheetWithDepth-stackSceneryContainer {
|
||||
/* INNER-LAYOUT */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.SheetWithDepth-stackSceneryFirstSheetBackdrop {
|
||||
/* SELF-LAYOUT */
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
inset: 0;
|
||||
|
||||
/* APPEARANCE */
|
||||
background-color: rgb(0, 0, 0);
|
||||
opacity: 0;
|
||||
|
||||
/* INTERACTIVITY */
|
||||
pointer-events: none;
|
||||
|
||||
/* MISCELLANEOUS */
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
/* Sheet */
|
||||
|
||||
.SheetWithDepth-view {
|
||||
/* SELF-LAYOUT */
|
||||
top: 0;
|
||||
bottom: initial;
|
||||
/* Adding 60px to make it fully visible below iOS Safari's
|
||||
bottom UI */
|
||||
height: calc(var(--silk-100-lvh-dvh-pct) + 60px);
|
||||
}
|
||||
|
||||
.SheetWithDepth-content {
|
||||
/* SELF-LAYOUT */
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - max(calc(env(safe-area-inset-top) + 1.3vh), 2.6vh));
|
||||
}
|
||||
|
||||
.SheetWithDepth-bleedingBackground {
|
||||
/* APPEARANCE */
|
||||
border-radius: 24px 24px 0 0;
|
||||
background-color: white;
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
432
packages/ui/src/silkhq/SheetWithDepth.tsx
Normal file
432
packages/ui/src/silkhq/SheetWithDepth.tsx
Normal file
@@ -0,0 +1,432 @@
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
Sheet,
|
||||
SheetStack,
|
||||
animate,
|
||||
useThemeColorDimmingOverlay,
|
||||
usePageScrollData,
|
||||
SheetViewProps,
|
||||
createComponentId,
|
||||
} from "@silk-hq/components";
|
||||
import "./SheetWithDepth.css";
|
||||
|
||||
// ================================================================================================
|
||||
// Stack Id
|
||||
// ================================================================================================
|
||||
|
||||
const sheetWithDepthStackId = createComponentId();
|
||||
|
||||
// ================================================================================================
|
||||
// StackRoot Context
|
||||
// ================================================================================================
|
||||
|
||||
type SheetWithDepthStackRootContextValue = {
|
||||
stackBackgroundRef: React.RefObject<HTMLDivElement>;
|
||||
stackFirstSheetBackdropRef: React.RefObject<HTMLDivElement>;
|
||||
stackingCount: number;
|
||||
setStackingCount: React.Dispatch<React.SetStateAction<number>>;
|
||||
};
|
||||
|
||||
const SheetWithDepthStackRootContext = createContext<SheetWithDepthStackRootContextValue | null>(
|
||||
null
|
||||
);
|
||||
const useSheetWithDepthStackRootContext = () => {
|
||||
const context = useContext(SheetWithDepthStackRootContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSheetWithDepthStackRootContext must be used within a SheetWithDepthStackRootContext"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// View Context
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDepthViewContext = createContext<{
|
||||
indexInStack: number;
|
||||
} | null>(null);
|
||||
const useSheetWithDepthViewContext = () => {
|
||||
const context = useContext(SheetWithDepthViewContext);
|
||||
if (!context) {
|
||||
throw new Error("useSheetWithDepthViewContext must be used within a SheetWithDepthViewContext");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// StackRoot
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDepthStackRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SheetStack.Root>,
|
||||
React.ComponentProps<typeof SheetStack.Root>
|
||||
>(({ children, ...restProps }, ref) => {
|
||||
const stackBackgroundRef = useRef<HTMLDivElement | null>(null);
|
||||
const stackFirstSheetBackdropRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const [stackingCount, setStackingCount] = useState(0);
|
||||
|
||||
const contextValue = useMemo(
|
||||
() => ({
|
||||
stackBackgroundRef,
|
||||
stackFirstSheetBackdropRef,
|
||||
stackingCount,
|
||||
setStackingCount,
|
||||
}),
|
||||
[stackingCount]
|
||||
);
|
||||
|
||||
return (
|
||||
<SheetWithDepthStackRootContext.Provider value={contextValue}>
|
||||
<SheetStack.Root componentId={sheetWithDepthStackId} {...restProps} ref={ref}>
|
||||
{children}
|
||||
</SheetStack.Root>
|
||||
</SheetWithDepthStackRootContext.Provider>
|
||||
);
|
||||
});
|
||||
SheetWithDepthStackRoot.displayName = "SheetWithDepthStack.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// StackSceneryOutlets
|
||||
// ================================================================================================
|
||||
|
||||
// The SheetStack outlets that define the scenery of the stack
|
||||
// (i.e. the content underneath) for the depth effect.
|
||||
|
||||
const initialTopOffset = "max(env(safe-area-inset-top), 1.3vh)";
|
||||
|
||||
const SheetWithDepthStackSceneryOutlets = React.forwardRef<
|
||||
React.ElementRef<typeof SheetStack.Outlet>,
|
||||
Omit<React.ComponentProps<typeof SheetStack.Outlet>, "asChild">
|
||||
>(({ children, className, stackingAnimation: stackingAnimationFromProps, ...restProps }, ref) => {
|
||||
const { stackBackgroundRef, stackFirstSheetBackdropRef } = useSheetWithDepthStackRootContext();
|
||||
|
||||
const { nativePageScrollReplaced } = usePageScrollData();
|
||||
|
||||
const [iOSStandalone, setiOSStandalone] = useState(false);
|
||||
useEffect(() => {
|
||||
setiOSStandalone(
|
||||
// @ts-ignore
|
||||
window.navigator.standalone && window.navigator.userAgent?.match(/iPhone|iPad/i)
|
||||
);
|
||||
}, []);
|
||||
|
||||
const stackingAnimation: React.ComponentPropsWithoutRef<
|
||||
typeof Sheet.Outlet
|
||||
>["stackingAnimation"] = {
|
||||
// Clipping & border-radius. We have a different animation
|
||||
// when the native page scroll is replaced, and in iOS
|
||||
// standalone mode.
|
||||
...(nativePageScrollReplaced
|
||||
? iOSStandalone
|
||||
? // In iOS standalone mode we don't need to animate the
|
||||
// border-radius because the corners are hidden by the
|
||||
// screen corners. So we just set the border-radius to
|
||||
// the needed value.
|
||||
{
|
||||
overflow: "clip",
|
||||
borderRadius: "24px",
|
||||
transformOrigin: "50% 0",
|
||||
}
|
||||
: // Outside of iOS standalone mode we do animate
|
||||
// the border-radius because the scenery is a visible
|
||||
// rectangle.
|
||||
{
|
||||
overflow: "clip",
|
||||
borderRadius: ({ progress }: any) => Math.min(progress * 24, 24) + "px",
|
||||
transformOrigin: "50% 0",
|
||||
}
|
||||
: // When the native page scroll is not replaced we
|
||||
// need to use the Silk's special clip properties to cut
|
||||
// off the rest of the page.
|
||||
{
|
||||
clipBoundary: "layout-viewport",
|
||||
clipBorderRadius: "24px",
|
||||
clipTransformOrigin: "50% 0",
|
||||
}),
|
||||
|
||||
// Translate & scale
|
||||
translateY: ({ progress }) =>
|
||||
progress <= 1
|
||||
? "calc(" + progress + " * " + initialTopOffset + ")"
|
||||
: // prettier-ignore
|
||||
"calc(" + initialTopOffset + " + 0.65vh * " + (progress - 1) + ")",
|
||||
scale: [1, 0.91],
|
||||
|
||||
// We merge animations coming from the props
|
||||
...stackingAnimationFromProps,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Element used as a black background representing the void under the stack. */}
|
||||
<div
|
||||
className={`SheetWithDepth-stackSceneryBackground nativePageScrollReplaced-${nativePageScrollReplaced}`}
|
||||
ref={stackBackgroundRef}
|
||||
/>
|
||||
{/* Element used as a container for the content under the stack. */}
|
||||
<SheetStack.Outlet
|
||||
className={`SheetWithDepth-stackSceneryContainer ${className ?? ""}`.trim()}
|
||||
forComponent={sheetWithDepthStackId}
|
||||
stackingAnimation={stackingAnimation}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
{/* Element used as the first sheet's backdrop, which only covers the stackSceneryContainer, not the entire viewport. */}
|
||||
<div
|
||||
className="SheetWithDepth-stackSceneryFirstSheetBackdrop"
|
||||
ref={stackFirstSheetBackdropRef}
|
||||
/>
|
||||
</SheetStack.Outlet>
|
||||
</>
|
||||
);
|
||||
});
|
||||
SheetWithDepthStackSceneryOutlets.displayName = "SheetWithDepthStack.SceneryOutlets";
|
||||
|
||||
// ================================================================================================
|
||||
// Root
|
||||
// ================================================================================================
|
||||
|
||||
type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
|
||||
type SheetWithDepthRootProps = Omit<SheetRootProps, "license"> & {
|
||||
license?: SheetRootProps["license"];
|
||||
};
|
||||
|
||||
const SheetWithDepthRoot = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Root>,
|
||||
SheetWithDepthRootProps
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<Sheet.Root license="commercial" forComponent={sheetWithDepthStackId} {...props} ref={ref} />
|
||||
);
|
||||
});
|
||||
SheetWithDepthRoot.displayName = "SheetWithDepth.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// View
|
||||
// ================================================================================================
|
||||
|
||||
// We use animate(), animateDimmingOverlayOpacity() and the
|
||||
// travelHandler instead of relying on stackingAnimation for the
|
||||
// stackSceneryBackground and stackSceneryFirstSheetBackdrop
|
||||
// elements in order to have a different (the default CSS
|
||||
// "ease"), less abrupt animation easing for them.
|
||||
|
||||
const SheetWithDepthView = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.View>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.View>
|
||||
>(
|
||||
(
|
||||
{ children, className, onTravelStatusChange, onTravel: travelHandlerFromProps, ...restProps },
|
||||
ref
|
||||
) => {
|
||||
const {
|
||||
stackingCount,
|
||||
setStackingCount,
|
||||
|
||||
stackBackgroundRef,
|
||||
stackFirstSheetBackdropRef,
|
||||
} = useSheetWithDepthStackRootContext();
|
||||
|
||||
const [indexInStack, setIndexInStack] = useState(0);
|
||||
const [travelStatus, setTravelStatus] = useState("idleOutside");
|
||||
|
||||
//
|
||||
// Define a dimming overlay
|
||||
|
||||
const { setDimmingOverlayOpacity, animateDimmingOverlayOpacity } = useThemeColorDimmingOverlay({
|
||||
elementRef: stackBackgroundRef,
|
||||
dimmingColor: "rgb(0, 0, 0)",
|
||||
});
|
||||
|
||||
//
|
||||
// travelStatusChangeHandler
|
||||
|
||||
const travelStatusChangeHandler = useCallback<
|
||||
NonNullable<SheetViewProps["onTravelStatusChange"]>
|
||||
>(
|
||||
(newTravelStatus) => {
|
||||
// Set indexInStack & stackingCount
|
||||
if (travelStatus !== "stepping" && newTravelStatus === "idleInside") {
|
||||
setStackingCount((prevStackingCount: number) => prevStackingCount + 1);
|
||||
if (indexInStack === 0) {
|
||||
setIndexInStack(stackingCount + 1);
|
||||
}
|
||||
}
|
||||
//
|
||||
else if (newTravelStatus === "idleOutside") {
|
||||
setStackingCount((prevStackingCount: number) => prevStackingCount - 1);
|
||||
setIndexInStack(0);
|
||||
}
|
||||
|
||||
// Animate on entering
|
||||
if (newTravelStatus === "entering" && stackingCount === 0) {
|
||||
animateDimmingOverlayOpacity({ keyframes: [0, 1] });
|
||||
animate(stackFirstSheetBackdropRef.current as HTMLElement, {
|
||||
opacity: [0, 0.33],
|
||||
});
|
||||
}
|
||||
|
||||
// Animate on exiting
|
||||
if (newTravelStatus === "exiting" && stackingCount === 1) {
|
||||
animateDimmingOverlayOpacity({ keyframes: [1, 0] });
|
||||
animate(stackFirstSheetBackdropRef.current as HTMLElement, {
|
||||
opacity: [0.33, 0],
|
||||
});
|
||||
}
|
||||
|
||||
// Set the state
|
||||
onTravelStatusChange?.(newTravelStatus);
|
||||
setTravelStatus(newTravelStatus);
|
||||
},
|
||||
[
|
||||
travelStatus,
|
||||
indexInStack,
|
||||
stackingCount,
|
||||
setStackingCount,
|
||||
stackFirstSheetBackdropRef,
|
||||
animateDimmingOverlayOpacity,
|
||||
onTravelStatusChange,
|
||||
]
|
||||
);
|
||||
|
||||
//
|
||||
// travelHandler
|
||||
|
||||
const travelHandler = useMemo(() => {
|
||||
if (indexInStack === 1 && travelStatus !== "entering" && travelStatus !== "exiting") {
|
||||
const handler: NonNullable<SheetViewProps["onTravel"]> = ({ progress, ...rest }) => {
|
||||
setDimmingOverlayOpacity(progress);
|
||||
stackFirstSheetBackdropRef.current?.style.setProperty(
|
||||
"opacity",
|
||||
(progress * 0.33) as unknown as string
|
||||
);
|
||||
travelHandlerFromProps?.({ progress, ...rest });
|
||||
};
|
||||
return handler;
|
||||
} else {
|
||||
return travelHandlerFromProps;
|
||||
}
|
||||
}, [indexInStack, travelStatus, stackFirstSheetBackdropRef, setDimmingOverlayOpacity]);
|
||||
|
||||
//
|
||||
// Return
|
||||
|
||||
return (
|
||||
<SheetWithDepthViewContext.Provider value={{ indexInStack }}>
|
||||
<Sheet.View
|
||||
className={`SheetWithDepth-view ${className ?? ""}`.trim()}
|
||||
contentPlacement="bottom"
|
||||
onTravelStatusChange={travelStatusChangeHandler}
|
||||
onTravel={travelHandler}
|
||||
nativeEdgeSwipePrevention={true}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Sheet.View>
|
||||
</SheetWithDepthViewContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
SheetWithDepthView.displayName = "SheetWithDepth.View";
|
||||
|
||||
// ================================================================================================
|
||||
// Backdrop
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDepthBackdrop = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Backdrop>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
|
||||
// @ts-ignore
|
||||
>(({ className, ...restProps }, ref) => {
|
||||
const { stackingCount } = useSheetWithDepthStackRootContext();
|
||||
const { indexInStack } = useSheetWithDepthViewContext();
|
||||
|
||||
return (
|
||||
// We don't render the Backdrop for the first sheet in the
|
||||
// stack, instead we use the stackSceneryFirstSheetBackdrop
|
||||
// element.
|
||||
stackingCount > 0 &&
|
||||
indexInStack !== 1 && (
|
||||
<Sheet.Backdrop
|
||||
className={`SheetWithDepth-backdrop ${className ?? ""}`.trim()}
|
||||
travelAnimation={{ opacity: [0, 0.33] }}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
);
|
||||
});
|
||||
SheetWithDepthBackdrop.displayName = "SheetWithDepth.Backdrop";
|
||||
|
||||
// ================================================================================================
|
||||
// Content
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDepthContent = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Content>,
|
||||
React.ComponentProps<typeof Sheet.Content>
|
||||
>(({ children, className, stackingAnimation, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Content
|
||||
className={`SheetWithDepth-content ${className ?? ""}`.trim()}
|
||||
stackingAnimation={{
|
||||
translateY: ({ progress }) =>
|
||||
progress <= 1
|
||||
? progress * -1.3 + "vh"
|
||||
: // prettier-ignore
|
||||
"calc(-1.3vh + 0.65vh * " + (progress - 1) + ")",
|
||||
scale: [1, 0.91],
|
||||
transformOrigin: "50% 0",
|
||||
...stackingAnimation,
|
||||
}}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
<Sheet.BleedingBackground className="SheetWithDepth-bleedingBackground" />
|
||||
{children}
|
||||
</Sheet.Content>
|
||||
);
|
||||
});
|
||||
SheetWithDepthContent.displayName = "SheetWithDepth.Content";
|
||||
|
||||
// ================================================================================================
|
||||
// Unchanged components
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDepthPortal = Sheet.Portal;
|
||||
const SheetWithDepthTrigger = Sheet.Trigger;
|
||||
const SheetWithDepthHandle = Sheet.Handle;
|
||||
const SheetWithDepthOutlet = Sheet.Outlet;
|
||||
const SheetWithDepthTitle = Sheet.Title;
|
||||
const SheetWithDepthDescription = Sheet.Description;
|
||||
|
||||
export const SheetWithDepthStack = {
|
||||
Root: SheetWithDepthStackRoot,
|
||||
SceneryOutlets: SheetWithDepthStackSceneryOutlets,
|
||||
};
|
||||
|
||||
export const SheetWithDepth = {
|
||||
Root: SheetWithDepthRoot,
|
||||
Portal: SheetWithDepthPortal,
|
||||
View: SheetWithDepthView,
|
||||
Content: SheetWithDepthContent,
|
||||
Backdrop: SheetWithDepthBackdrop,
|
||||
Trigger: SheetWithDepthTrigger,
|
||||
Handle: SheetWithDepthHandle,
|
||||
Outlet: SheetWithDepthOutlet,
|
||||
Title: SheetWithDepthTitle,
|
||||
Description: SheetWithDepthDescription,
|
||||
};
|
||||
45
packages/ui/src/silkhq/SheetWithDetent.css
Normal file
45
packages/ui/src/silkhq/SheetWithDetent.css
Normal file
@@ -0,0 +1,45 @@
|
||||
.SheetWithDetent-view {
|
||||
/* SELF-LAYOUT */
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
bottom: initial;
|
||||
/* Adding 60px to make it fully visible below iOS Safari's
|
||||
bottom UI */
|
||||
height: calc(var(--silk-100-lvh-dvh-pct) + 60px);
|
||||
}
|
||||
|
||||
.SheetWithDetent-content {
|
||||
/* SELF-LAYOUT */
|
||||
box-sizing: border-box;
|
||||
height: calc(100% - max(env(safe-area-inset-top), 6px));
|
||||
max-width: 800px;
|
||||
|
||||
/* APPEARANCE */
|
||||
border-radius: 24px 24px 0 0;
|
||||
overflow: hidden;
|
||||
background-color: white;
|
||||
}
|
||||
@media (min-width: 800px) {
|
||||
.SheetWithDetent-content {
|
||||
/* SELF-LAYOUT */
|
||||
height: calc(100% - max(env(safe-area-inset-top), 5vh));
|
||||
|
||||
/* APPEARANCE */
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
border-radius: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.SheetWithDetent-handle {
|
||||
/* SELF-LAYOUT */
|
||||
width: 50px;
|
||||
height: 6px;
|
||||
|
||||
/* APPEARANCE */
|
||||
border: 0;
|
||||
border-radius: 9999px;
|
||||
background-color: rgb(209, 213, 219);
|
||||
|
||||
/* INTERACTIVITY */
|
||||
cursor: pointer;
|
||||
}
|
||||
275
packages/ui/src/silkhq/SheetWithDetent.tsx
Normal file
275
packages/ui/src/silkhq/SheetWithDetent.tsx
Normal file
@@ -0,0 +1,275 @@
|
||||
import React, { createContext, useContext, useMemo, useRef, useState } from "react";
|
||||
import { Sheet, Scroll, type SheetViewProps } from "@silk-hq/components";
|
||||
import "./SheetWithDetent.css";
|
||||
|
||||
// ================================================================================================
|
||||
// Context
|
||||
// ================================================================================================
|
||||
|
||||
type SheetWithDetentContextValue = {
|
||||
reachedLastDetent: boolean;
|
||||
setReachedLastDetent: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
viewRef: React.RefObject<HTMLElement>;
|
||||
};
|
||||
|
||||
const SheetWithDetentContext = createContext<SheetWithDetentContextValue | null>(null);
|
||||
|
||||
const useSheetWithDetentContext = () => {
|
||||
const context = useContext(SheetWithDetentContext);
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
"useSheetWithDetentContext must be used within a SheetWithDetentContextProvider"
|
||||
);
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// ================================================================================================
|
||||
// Root
|
||||
// ================================================================================================
|
||||
|
||||
type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
|
||||
type SheetWithDetentRootProps = Omit<SheetRootProps, "license"> & {
|
||||
license?: SheetRootProps["license"];
|
||||
};
|
||||
|
||||
const SheetWithDetentRoot = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Root>,
|
||||
SheetWithDetentRootProps
|
||||
>(({ children, ...restProps }, ref) => {
|
||||
const [reachedLastDetent, setReachedLastDetent] = useState(false);
|
||||
const viewRef = useRef<HTMLElement>(null);
|
||||
|
||||
return (
|
||||
<SheetWithDetentContext.Provider
|
||||
value={{
|
||||
reachedLastDetent,
|
||||
setReachedLastDetent,
|
||||
viewRef,
|
||||
}}
|
||||
>
|
||||
<Sheet.Root license="commercial" {...restProps} ref={ref}>
|
||||
{children}
|
||||
</Sheet.Root>
|
||||
</SheetWithDetentContext.Provider>
|
||||
);
|
||||
});
|
||||
SheetWithDetentRoot.displayName = "SheetWithDetent.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// View
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentView = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.View>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.View>
|
||||
>(
|
||||
(
|
||||
{ children, className, onTravelStatusChange, onTravelRangeChange, onTravel, ...restProps },
|
||||
ref
|
||||
) => {
|
||||
const { reachedLastDetent, setReachedLastDetent, viewRef } = useSheetWithDetentContext();
|
||||
|
||||
//
|
||||
|
||||
const travelHandler = useMemo(() => {
|
||||
if (!reachedLastDetent) return onTravel;
|
||||
|
||||
const handler: SheetViewProps["onTravel"] = ({ progress, ...rest }) => {
|
||||
if (!viewRef.current) return onTravel?.({ progress, ...rest });
|
||||
|
||||
// Dismiss the on-screen keyboard.
|
||||
if (progress < 0.999) {
|
||||
viewRef.current.focus();
|
||||
}
|
||||
onTravel?.({ progress, ...rest });
|
||||
};
|
||||
return handler;
|
||||
}, [reachedLastDetent, onTravel, viewRef]);
|
||||
|
||||
//
|
||||
|
||||
const setRefs = React.useCallback((node: HTMLElement | null) => {
|
||||
// @ts-ignore - intentionally breaking the readonly nature for compatibility
|
||||
viewRef.current = node;
|
||||
|
||||
if (typeof ref === "function") {
|
||||
ref(node);
|
||||
} else if (ref) {
|
||||
ref.current = node;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Sheet.View
|
||||
className={`SheetWithDetent-view ${className ?? ""}`.trim()}
|
||||
detents={!reachedLastDetent ? "66vh" : undefined}
|
||||
swipeOvershoot={false}
|
||||
nativeEdgeSwipePrevention={true}
|
||||
onTravelStatusChange={(travelStatus) => {
|
||||
if (travelStatus === "idleOutside") setReachedLastDetent(false);
|
||||
onTravelStatusChange?.(travelStatus);
|
||||
}}
|
||||
onTravelRangeChange={(range) => {
|
||||
if (range.start === 2) setReachedLastDetent(true);
|
||||
onTravelRangeChange?.(range);
|
||||
}}
|
||||
onTravel={travelHandler}
|
||||
ref={setRefs}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</Sheet.View>
|
||||
);
|
||||
}
|
||||
);
|
||||
SheetWithDetentView.displayName = "SheetWithDetent.View";
|
||||
|
||||
// ================================================================================================
|
||||
// Backdrop
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentBackdrop = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Backdrop>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
|
||||
>(({ className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Backdrop
|
||||
className={`SheetWithDetent-backdrop ${className ?? ""}`.trim()}
|
||||
themeColorDimming="auto"
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
SheetWithDetentBackdrop.displayName = "SheetWithDetent.Backdrop";
|
||||
|
||||
// ================================================================================================
|
||||
// Content
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentContent = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Content>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Sheet.Content
|
||||
className={`SheetWithDetent-content ${className ?? ""}`.trim()}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Sheet.Content>
|
||||
);
|
||||
});
|
||||
SheetWithDetentContent.displayName = "SheetWithDetent.Content";
|
||||
|
||||
// ================================================================================================
|
||||
// Handle
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentHandle = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Handle>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Handle>
|
||||
>(({ className, ...restProps }, ref) => {
|
||||
const { reachedLastDetent } = useSheetWithDetentContext();
|
||||
|
||||
return (
|
||||
<Sheet.Handle
|
||||
className={`SheetWithDetent-handle ${className ?? ""}`.trim()}
|
||||
action={reachedLastDetent ? "dismiss" : "step"}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
SheetWithDetentHandle.displayName = "SheetWithDetent.Handle";
|
||||
|
||||
// ================================================================================================
|
||||
// Scroll Root
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentScrollRoot = React.forwardRef<
|
||||
React.ElementRef<typeof Scroll.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof Scroll.Root>
|
||||
>(({ children, ...restProps }, ref) => {
|
||||
return (
|
||||
<Scroll.Root {...restProps} ref={ref}>
|
||||
{children}
|
||||
</Scroll.Root>
|
||||
);
|
||||
});
|
||||
SheetWithDetentScrollRoot.displayName = "SheetWithDetent.ScrollRoot";
|
||||
|
||||
// ================================================================================================
|
||||
// Scroll View
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentScrollView = React.forwardRef<
|
||||
React.ElementRef<typeof Scroll.View>,
|
||||
React.ComponentPropsWithoutRef<typeof Scroll.View>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
const { reachedLastDetent } = useSheetWithDetentContext();
|
||||
|
||||
return (
|
||||
<Scroll.View
|
||||
className={`SheetWithDetent-scrollView ${className ?? ""}`.trim()}
|
||||
scrollGestureTrap={{ yEnd: true }}
|
||||
scrollGesture={!reachedLastDetent ? false : "auto"}
|
||||
safeArea="layout-viewport"
|
||||
onScrollStart={{ dismissKeyboard: true }}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Scroll.View>
|
||||
);
|
||||
});
|
||||
SheetWithDetentScrollView.displayName = "SheetWithDetent.ScrollView";
|
||||
|
||||
// ================================================================================================
|
||||
// Scroll Content
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentScrollContent = React.forwardRef<
|
||||
React.ElementRef<typeof Scroll.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof Scroll.Content>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
return (
|
||||
<Scroll.Content
|
||||
className={`SheetWithDetent-scrollContent ${className ?? ""}`.trim()}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Scroll.Content>
|
||||
);
|
||||
});
|
||||
SheetWithDetentScrollContent.displayName = "SheetWithDetent.ScrollContent";
|
||||
|
||||
// ================================================================================================
|
||||
// Unchanged Components
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithDetentPortal = Sheet.Portal;
|
||||
const SheetWithDetentTrigger = Sheet.Trigger;
|
||||
const SheetWithDetentOutlet = Sheet.Outlet;
|
||||
const SheetWithDetentTitle = Sheet.Title;
|
||||
const SheetWithDetentDescription = Sheet.Description;
|
||||
|
||||
export const SheetWithDetent = {
|
||||
Root: SheetWithDetentRoot,
|
||||
Portal: SheetWithDetentPortal,
|
||||
View: SheetWithDetentView,
|
||||
Backdrop: SheetWithDetentBackdrop,
|
||||
Content: SheetWithDetentContent,
|
||||
Trigger: SheetWithDetentTrigger,
|
||||
Handle: SheetWithDetentHandle,
|
||||
Outlet: SheetWithDetentOutlet,
|
||||
Title: SheetWithDetentTitle,
|
||||
Description: SheetWithDetentDescription,
|
||||
//
|
||||
ScrollRoot: SheetWithDetentScrollRoot,
|
||||
ScrollView: SheetWithDetentScrollView,
|
||||
ScrollContent: SheetWithDetentScrollContent,
|
||||
};
|
||||
46
packages/ui/src/silkhq/SheetWithStacking.css
Normal file
46
packages/ui/src/silkhq/SheetWithStacking.css
Normal file
@@ -0,0 +1,46 @@
|
||||
.SheetWithStacking-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);
|
||||
}
|
||||
.SheetWithStacking-view.contentPlacement-right {
|
||||
/* SELF-LAYOUT */
|
||||
height: var(--silk-100-lvh-dvh-pct);
|
||||
}
|
||||
|
||||
.SheetWithStacking-content {
|
||||
/* SELF-LAYOUT */
|
||||
box-sizing: border-box;
|
||||
height: calc(min(500px, 90svh) + env(safe-area-inset-bottom, 0px));
|
||||
|
||||
/* APPEARANCE */
|
||||
background-color: transparent;
|
||||
|
||||
/* INNER-LAYOUT */
|
||||
padding-inline: 0.5rem;
|
||||
padding-block: 0.5rem max(env(safe-area-inset-bottom, 0px), 0.5rem);
|
||||
display: grid;
|
||||
}
|
||||
.SheetWithStacking-content.contentPlacement-right {
|
||||
/* SELF-LAYOUT */
|
||||
height: 100%;
|
||||
width: min(80%, 700px);
|
||||
|
||||
/* INNER-LAYOUT */
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.SheetWithStacking-innerContent {
|
||||
/* SELF-LAYOUT */
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
|
||||
/* APPEARANCE */
|
||||
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
border-radius: 24px;
|
||||
background-color: white;
|
||||
}
|
||||
199
packages/ui/src/silkhq/SheetWithStacking.tsx
Normal file
199
packages/ui/src/silkhq/SheetWithStacking.tsx
Normal file
@@ -0,0 +1,199 @@
|
||||
import React, { createContext, useContext } from "react";
|
||||
import {
|
||||
Sheet,
|
||||
SheetStack,
|
||||
useClientMediaQuery,
|
||||
type SheetContentProps,
|
||||
} from "@silk-hq/components";
|
||||
import "./SheetWithStacking.css";
|
||||
|
||||
// ================================================================================================
|
||||
// Context
|
||||
// ================================================================================================
|
||||
|
||||
type SheetWithStackingContextValue = {
|
||||
travelStatus: string;
|
||||
setTravelStatus: (status: string) => void;
|
||||
contentPlacement: "right" | "bottom";
|
||||
};
|
||||
|
||||
const SheetWithStackingContext = createContext<SheetWithStackingContextValue | null>(null);
|
||||
|
||||
// ================================================================================================
|
||||
// Stack Root
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithStackingStackRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SheetStack.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetStack.Root>
|
||||
>(({ children, ...restProps }, ref) => {
|
||||
return (
|
||||
<SheetStack.Root {...restProps} ref={ref}>
|
||||
{children}
|
||||
</SheetStack.Root>
|
||||
);
|
||||
});
|
||||
SheetWithStackingStackRoot.displayName = "SheetWithStackingStack.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// Root
|
||||
// ================================================================================================
|
||||
|
||||
type SheetRootProps = React.ComponentPropsWithoutRef<typeof Sheet.Root>;
|
||||
type SheetWithStackingRootProps = Omit<SheetRootProps, "license"> & {
|
||||
license?: SheetRootProps["license"];
|
||||
};
|
||||
|
||||
const SheetWithStackingRoot = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Root>,
|
||||
SheetWithStackingRootProps
|
||||
>(({ children, ...restProps }, ref) => {
|
||||
const [travelStatus, setTravelStatus] = React.useState("idleOutside");
|
||||
const largeViewport = useClientMediaQuery("(min-width: 700px)");
|
||||
const contentPlacement = largeViewport ? "right" : "bottom";
|
||||
|
||||
return (
|
||||
<SheetWithStackingContext.Provider
|
||||
value={{
|
||||
travelStatus,
|
||||
setTravelStatus,
|
||||
contentPlacement,
|
||||
}}
|
||||
>
|
||||
<Sheet.Root license="commercial" forComponent="closest" {...restProps} ref={ref}>
|
||||
{children}
|
||||
</Sheet.Root>
|
||||
</SheetWithStackingContext.Provider>
|
||||
);
|
||||
});
|
||||
SheetWithStackingRoot.displayName = "SheetWithStacking.Root";
|
||||
|
||||
// ================================================================================================
|
||||
// View
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithStackingView = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.View>
|
||||
>(({ children, className, ...restProps }, ref) => {
|
||||
const context = useContext(SheetWithStackingContext);
|
||||
if (!context)
|
||||
throw new Error(
|
||||
"SheetWithStackingView must be used within a SheetWithStackingContext.Provider"
|
||||
);
|
||||
const { setTravelStatus, contentPlacement } = context;
|
||||
|
||||
return (
|
||||
<Sheet.View
|
||||
className={`SheetWithStacking-view contentPlacement-${contentPlacement} ${className ?? ""}`}
|
||||
contentPlacement={contentPlacement}
|
||||
nativeEdgeSwipePrevention={true}
|
||||
onTravelStatusChange={setTravelStatus}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Sheet.View>
|
||||
);
|
||||
});
|
||||
SheetWithStackingView.displayName = "SheetWithStacking.View";
|
||||
|
||||
// ================================================================================================
|
||||
// Backdrop
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithStackingBackdrop = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Backdrop>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Backdrop>
|
||||
>((props, ref) => {
|
||||
return (
|
||||
<Sheet.Backdrop
|
||||
travelAnimation={{ opacity: [0, 0.2] }}
|
||||
themeColorDimming="auto"
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
});
|
||||
SheetWithStackingBackdrop.displayName = "SheetWithStacking.Backdrop";
|
||||
|
||||
// ================================================================================================
|
||||
// Content
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithStackingContent = React.forwardRef<
|
||||
React.ElementRef<typeof Sheet.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof Sheet.Content>
|
||||
>(({ children, className, stackingAnimation: stackingAnimationFromProps, ...restProps }, ref) => {
|
||||
const context = useContext(SheetWithStackingContext);
|
||||
if (!context)
|
||||
throw new Error(
|
||||
"SheetWithStackingContent must be used within a SheetWithStackingContext.Provider"
|
||||
);
|
||||
const { contentPlacement } = context;
|
||||
|
||||
const stackingAnimation: SheetContentProps["stackingAnimation"] =
|
||||
contentPlacement === "right"
|
||||
? {
|
||||
translateX: ({ progress }: { progress: number }) =>
|
||||
progress <= 1
|
||||
? progress * -10 + "px"
|
||||
: // prettier-ignore
|
||||
"calc(-12.5px + 2.5px *" + progress + ")",
|
||||
scale: [1, 0.933],
|
||||
transformOrigin: "0 50%",
|
||||
...stackingAnimationFromProps,
|
||||
}
|
||||
: {
|
||||
translateY: ({ progress }: { progress: number }) =>
|
||||
progress <= 1
|
||||
? progress * -10 + "px"
|
||||
: // prettier-ignore
|
||||
"calc(-12.5px + 2.5px *" + progress + ")",
|
||||
scale: [1, 0.933],
|
||||
transformOrigin: "50% 0",
|
||||
...stackingAnimationFromProps,
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet.Content
|
||||
className={`SheetWithStacking-content contentPlacement-${contentPlacement} ${
|
||||
className ?? ""
|
||||
}`}
|
||||
stackingAnimation={stackingAnimation}
|
||||
{...restProps}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="SheetWithStacking-innerContent">{children}</div>
|
||||
</Sheet.Content>
|
||||
);
|
||||
});
|
||||
SheetWithStackingContent.displayName = "SheetWithStacking.Content";
|
||||
|
||||
// ================================================================================================
|
||||
// Unchanged Components
|
||||
// ================================================================================================
|
||||
|
||||
const SheetWithStackingPortal = Sheet.Portal;
|
||||
const SheetWithStackingTrigger = Sheet.Trigger;
|
||||
const SheetWithStackingHandle = Sheet.Handle;
|
||||
const SheetWithStackingOutlet = Sheet.Outlet;
|
||||
const SheetWithStackingTitle = Sheet.Title;
|
||||
const SheetWithStackingDescription = Sheet.Description;
|
||||
|
||||
export const SheetWithStackingStack = {
|
||||
Root: SheetWithStackingStackRoot,
|
||||
};
|
||||
|
||||
export const SheetWithStacking = {
|
||||
Root: SheetWithStackingRoot,
|
||||
View: SheetWithStackingView,
|
||||
Portal: SheetWithStackingPortal,
|
||||
Backdrop: SheetWithStackingBackdrop,
|
||||
Content: SheetWithStackingContent,
|
||||
Trigger: SheetWithStackingTrigger,
|
||||
Handle: SheetWithStackingHandle,
|
||||
Outlet: SheetWithStackingOutlet,
|
||||
Title: SheetWithStackingTitle,
|
||||
Description: SheetWithStackingDescription,
|
||||
};
|
||||
21
packages/ui/src/silkhq/silkhq.ts
Normal file
21
packages/ui/src/silkhq/silkhq.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import "@silk-hq/components/dist/main-unlayered.css"
|
||||
import { Sheet } from '@silk-hq/components'
|
||||
import { BottomSheet } from './BottomSheet'
|
||||
import { SheetWithDepth, SheetWithDepthStack } from './SheetWithDepth'
|
||||
import { SheetWithDetent } from './SheetWithDetent'
|
||||
import { SheetWithStacking, SheetWithStackingStack } from './SheetWithStacking'
|
||||
|
||||
declare global {
|
||||
var LSSilkhq: any
|
||||
}
|
||||
|
||||
const silkhq = {
|
||||
Sheet, BottomSheet,
|
||||
SheetWithDepth, SheetWithDepthStack,
|
||||
SheetWithStacking, SheetWithDetent,
|
||||
SheetWithStackingStack,
|
||||
}
|
||||
|
||||
window.LSSilkhq = silkhq
|
||||
|
||||
export default silkhq
|
||||
@@ -3157,6 +3157,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz#1973871850856ae72bc678aeb066ab952330e923"
|
||||
integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==
|
||||
|
||||
"@silk-hq/components@^0.9.10":
|
||||
version "0.9.10"
|
||||
resolved "https://registry.yarnpkg.com/@silk-hq/components/-/components-0.9.10.tgz#ed6baa898b4f36ce0e5ecadabfecef748546db74"
|
||||
integrity sha512-dr6NRdGR2vovh4Uv27IhnkvpcUwHR9D7YZLCxTE6fyl4Zb6K6cGUlWVo3b3tgfCHVyirvrRvqWOF2nxMVlmVXg==
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
|
||||
Reference in New Issue
Block a user