;
@@ -17,15 +18,18 @@ function PopoverContent({
alignOffset = 0,
side = "bottom",
sideOffset = 4,
+ collisionPadding,
portalProps,
...props
}: PopoverPrimitive.Popup.Props &
Pick<
PopoverPrimitive.Positioner.Props,
- "align" | "alignOffset" | "side" | "sideOffset"
+ "align" | "alignOffset" | "side" | "sideOffset" | "collisionPadding"
> & {
portalProps?: PopoverPrimitive.Portal.Props;
}) {
+ const safeAreaCollisionPadding = useSafeAreaCollisionPadding();
+
return (
) {
+ const safeAreaCollisionPadding = useSafeAreaCollisionPadding();
+
return (
}
diff --git a/src/cmp/sidebar.tsx b/src/cmp/sidebar.tsx
index a4ab442..f13ee00 100644
--- a/src/cmp/sidebar.tsx
+++ b/src/cmp/sidebar.tsx
@@ -215,10 +215,10 @@ function Sidebar({
data-side={side}
style={{ width: SIDEBAR_WIDTH }}
className={cn(
- "fixed inset-y-0 z-10 hidden h-svh transition-[left,right,width,transform] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:group-data-[collapsible=offcanvas]:-translate-x-full data-[side=right]:right-0 data-[side=right]:group-data-[collapsible=offcanvas]:translate-x-full md:flex",
+ "fixed inset-y-0 z-10 hidden h-svh pt-[var(--ui-safe-area-top)] pb-[var(--ui-safe-area-bottom)] transition-[left,right,width,transform] duration-200 ease-linear data-[side=left]:left-0 data-[side=left]:pl-[var(--ui-safe-area-left)] data-[side=left]:group-data-[collapsible=offcanvas]:-translate-x-full data-[side=right]:right-0 data-[side=right]:pr-[var(--ui-safe-area-right)] data-[side=right]:group-data-[collapsible=offcanvas]:translate-x-full md:flex",
// Adjust the padding for floating and inset variants.
variant === "floating" || variant === "inset"
- ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
+ ? "pt-[max(0.5rem,var(--ui-safe-area-top))] pr-2 pb-[max(0.5rem,var(--ui-safe-area-bottom))] pl-2 data-[side=left]:pl-[max(0.5rem,var(--ui-safe-area-left))] data-[side=right]:pr-[max(0.5rem,var(--ui-safe-area-right))] group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
: "group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
className,
)}
diff --git a/src/cmp/sonner.tsx b/src/cmp/sonner.tsx
index 7cbe96b..c9b1f80 100644
--- a/src/cmp/sonner.tsx
+++ b/src/cmp/sonner.tsx
@@ -17,6 +17,18 @@ const Toaster = ({ ...props }: ToasterProps) => {
,
info: ,
diff --git a/src/cmp/tooltip.tsx b/src/cmp/tooltip.tsx
index 927cc33..18e8ed0 100644
--- a/src/cmp/tooltip.tsx
+++ b/src/cmp/tooltip.tsx
@@ -2,6 +2,7 @@ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip";
import type { ReactElement, ReactNode } from "react";
import { cn } from "../lib/utils";
+import { useSafeAreaCollisionPadding } from "../hooks/use-safe-area";
function TooltipProvider({
delay = 0,
@@ -31,15 +32,18 @@ function TooltipContent({
align = "center",
alignOffset = 0,
children,
+ collisionPadding,
portalProps,
...props
}: TooltipPrimitive.Popup.Props &
Pick<
TooltipPrimitive.Positioner.Props,
- "align" | "alignOffset" | "side" | "sideOffset"
+ "align" | "alignOffset" | "side" | "sideOffset" | "collisionPadding"
> & {
portalProps?: TooltipPrimitive.Portal.Props;
}) {
+ const safeAreaCollisionPadding = useSafeAreaCollisionPadding();
+
return (
styles.getPropertyValue(property))
+ .join(",");
+}
+
+function subscribe(onChange: () => void) {
+ window.addEventListener("resize", onChange);
+ window.addEventListener("orientationchange", onChange);
+ window.visualViewport?.addEventListener("resize", onChange);
+
+ return () => {
+ window.removeEventListener("resize", onChange);
+ window.removeEventListener("orientationchange", onChange);
+ window.visualViewport?.removeEventListener("resize", onChange);
+ };
+}
+
+export function useSafeAreaCollisionPadding() {
+ const snapshot = React.useSyncExternalStore(
+ subscribe,
+ getSnapshot,
+ () => "0,0,0,0",
+ );
+ const [top, right, bottom, left] = snapshot
+ .split(",")
+ .map((value) => Math.max(5, Number.parseFloat(value) || 0));
+
+ return { top, right, bottom, left };
+}
diff --git a/src/index.css b/src/index.css
index 5d69834..bfab616 100644
--- a/src/index.css
+++ b/src/index.css
@@ -88,6 +88,10 @@
}
:root {
+ --ui-safe-area-top: env(safe-area-inset-top, 0px);
+ --ui-safe-area-right: env(safe-area-inset-right, 0px);
+ --ui-safe-area-bottom: env(safe-area-inset-bottom, 0px);
+ --ui-safe-area-left: env(safe-area-inset-left, 0px);
--ui-density: 1;
--ui-border-width: 1px;
--ui-shadow-strength: 0;
diff --git a/src/index.ts b/src/index.ts
index 5f12bbe..8f9df28 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -8,6 +8,7 @@ export { toast } from "sonner";
export * from "./theme";
export * from "./screens/util";
export * from "./hooks/use-mobile";
+export * from "./hooks/use-safe-area";
export * from "./lib/utils";
export * from "./cmp/accordion";
diff --git a/src/screens/util.tsx b/src/screens/util.tsx
index 733c85d..3cf491d 100644
--- a/src/screens/util.tsx
+++ b/src/screens/util.tsx
@@ -1,6 +1,5 @@
import { cn } from "../lib/utils";
import Controls from "../windowControls";
-import { isTauri } from "@tauri-apps/api/core";
export default function CreateScreen({
children,
@@ -12,13 +11,13 @@ export default function CreateScreen({
return (
<>
-
+
{children}
>
@@ -87,9 +86,8 @@ export function StorageDestoryer() {
return (