Move portal to shared ui lib

This commit is contained in:
Gregory Schier
2026-03-12 14:28:15 -07:00
parent 47f0daabff
commit cc504e0a1c
10 changed files with 52 additions and 14 deletions

View File

@@ -4,7 +4,7 @@ import type { ReactNode } from 'react';
import { hideToast, toastsAtom } from '../lib/toast';
import { Toast, type ToastProps } from './core/Toast';
import { ErrorBoundary } from './ErrorBoundary';
import { Portal } from './Portal';
import { Portal } from '@yaakapp-internal/ui';
export type ToastInstance = {
id: string;

View File

@@ -1,5 +1,6 @@
import { type } from '@tauri-apps/plugin-os';
import { settingsAtom, workspacesAtom } from '@yaakapp-internal/models';
import { HeaderSize, Overlay, SidebarLayout } from '@yaakapp-internal/ui';
import classNames from 'classnames';
import { useAtomValue } from 'jotai';
import * as m from 'motion/react-m';
@@ -39,15 +40,12 @@ import { HStack } from './core/Stacks';
import { ErrorBoundary } from './ErrorBoundary';
import { FolderLayout } from './FolderLayout';
import { GrpcConnectionLayout } from './GrpcConnectionLayout';
import { HeaderSize, SidebarLayout } from '@yaakapp-internal/ui';
import { HttpRequestLayout } from './HttpRequestLayout';
import { Overlay } from './Overlay';
import Sidebar from './Sidebar';
import { SidebarActions } from './SidebarActions';
import { WebsocketRequestLayout } from './WebsocketRequestLayout';
import { WorkspaceHeader } from './WorkspaceHeader';
const head = { gridArea: 'head' };
const body = { gridArea: 'body' };
export function Workspace() {
@@ -85,10 +83,7 @@ export function Workspace() {
interfaceScale={settings.interfaceScale}
>
<div className="absolute inset-0 pointer-events-none">
<div
style={environmentBgStyle}
className="absolute inset-0 opacity-[0.07]"
/>
<div style={environmentBgStyle} className="absolute inset-0 opacity-[0.07]" />
<div
style={environmentBgStyle}
className="absolute left-0 right-0 -bottom-[1px] h-[1px] opacity-20"
@@ -132,7 +127,15 @@ export function Workspace() {
'grid grid-rows-[auto_1fr]',
)}
>
<HeaderSize hideControls size="lg" className="border-transparent flex items-center" osType={osType} hideWindowControls={settings.hideWindowControls} useNativeTitlebar={settings.useNativeTitlebar} interfaceScale={settings.interfaceScale}>
<HeaderSize
hideControls
size="lg"
className="border-transparent flex items-center"
osType={osType}
hideWindowControls={settings.hideWindowControls}
useNativeTitlebar={settings.useNativeTitlebar}
interfaceScale={settings.interfaceScale}
>
<SidebarActions />
</HeaderSize>
<ErrorBoundary name="Sidebar (Floating)">

View File

@@ -2,7 +2,7 @@ import classNames from 'classnames';
import * as m from 'motion/react-m';
import type { ReactNode } from 'react';
import { useMemo } from 'react';
import { Overlay } from '../Overlay';
import { Overlay } from '@yaakapp-internal/ui';
import { Heading } from './Heading';
import { IconButton } from './IconButton';
import type { DialogSize } from '@yaakapp-internal/plugins';

View File

@@ -32,7 +32,7 @@ import { generateId } from '../../lib/generateId';
import { getNodeText } from '../../lib/getNodeText';
import { jotaiStore } from '../../lib/jotai';
import { ErrorBoundary } from '../ErrorBoundary';
import { Overlay } from '../Overlay';
import { Overlay } from '@yaakapp-internal/ui';
import { Button } from './Button';
import { Hotkey } from './Hotkey';
import { Icon, LoadingIcon, type IconProps } from '@yaakapp-internal/ui';

View File

@@ -2,7 +2,7 @@ import classNames from 'classnames';
import type { CSSProperties, KeyboardEvent, ReactNode } from 'react';
import { useRef, useState } from 'react';
import { generateId } from '../../lib/generateId';
import { Portal } from '../Portal';
import { Portal } from '@yaakapp-internal/ui';
export interface TooltipProps {
children: ReactNode;

View File

@@ -13,6 +13,8 @@ interface Props {
onWidthChange: (width: number) => void;
hidden?: boolean;
onHiddenChange?: (hidden: boolean) => void;
floating?: boolean;
floatingWidth?: number;
defaultWidth?: number;
minWidth?: number;
className?: string;
@@ -25,6 +27,8 @@ export function SidebarLayout({
onWidthChange,
hidden = false,
onHiddenChange,
floating = false,
floatingWidth = 320,
defaultWidth = 250,
minWidth = 50,
className,
@@ -75,6 +79,28 @@ export function SidebarLayout({
onWidthChange(defaultWidth);
}, [onWidthChange, defaultWidth]);
if (floating) {
return (
<div className={classNames(className, 'relative w-full h-full overflow-hidden')}>
{children}
{!hidden && (
<>
<div
className="absolute inset-0 bg-black/50 z-20 transition-opacity"
onClick={() => onHiddenChange?.(true)}
/>
<div
style={{ width: floatingWidth }}
className="absolute top-0 left-0 bottom-0 z-20 animate-slide-in-left"
>
{sidebar}
</div>
</>
)}
</div>
);
}
return (
<div
style={styles}

View File

@@ -8,10 +8,16 @@ export function usePortal(name: string) {
}
function getOrCreatePortal(name: string) {
const portalContainer = document.getElementById(PORTAL_CONTAINER_ID) as HTMLDivElement;
let portalContainer = document.getElementById(PORTAL_CONTAINER_ID);
if (!portalContainer) {
portalContainer = document.createElement('div');
portalContainer.id = PORTAL_CONTAINER_ID;
document.body.appendChild(portalContainer);
}
let existing = portalContainer.querySelector(`:scope > [data-portal-name="${name}"]`);
if (!existing) {
const el: HTMLDivElement = document.createElement('div');
const el = document.createElement('div');
el.setAttribute('data-portal-name', name);
portalContainer.appendChild(el);
existing = el;

View File

@@ -28,3 +28,6 @@ export type { SplitLayoutLayout, SlotProps } from "./components/SplitLayout";
export { Table, TableBody, TableHead, TableRow, TableCell, TruncatedWideTableCell, TableHeaderCell } from "./components/Table";
export { clamp } from "./lib/clamp";
export { useContainerSize } from "./hooks/useContainerSize";
export { Overlay } from "./components/Overlay";
export { Portal } from "./components/Portal";
export { usePortal } from "./hooks/usePortal";