mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-30 22:22:02 +02:00
Rework workspace header
This commit is contained in:
@@ -3,6 +3,7 @@ import { memo, useCallback, useMemo } from 'react';
|
|||||||
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
||||||
import { useAppRoutes } from '../hooks/useAppRoutes';
|
import { useAppRoutes } from '../hooks/useAppRoutes';
|
||||||
import { useEnvironments } from '../hooks/useEnvironments';
|
import { useEnvironments } from '../hooks/useEnvironments';
|
||||||
|
import type { ButtonProps } from './core/Button';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import type { DropdownItem } from './core/Dropdown';
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
@@ -12,10 +13,11 @@ import { EnvironmentEditDialog } from './EnvironmentEditDialog';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
} & Pick<ButtonProps, 'forDropdown' | 'leftSlot'>;
|
||||||
|
|
||||||
export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdown({
|
export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdown({
|
||||||
className,
|
className,
|
||||||
|
...buttonProps
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const environments = useEnvironments();
|
const environments = useEnvironments();
|
||||||
const activeEnvironment = useActiveEnvironment();
|
const activeEnvironment = useActiveEnvironment();
|
||||||
@@ -62,13 +64,13 @@ export const EnvironmentActionsDropdown = memo(function EnvironmentActionsDropdo
|
|||||||
return (
|
return (
|
||||||
<Dropdown items={items}>
|
<Dropdown items={items}>
|
||||||
<Button
|
<Button
|
||||||
forDropdown
|
|
||||||
size="sm"
|
size="sm"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'text-gray-800 !px-2 truncate',
|
'text-gray-800 !px-2 truncate',
|
||||||
activeEnvironment == null && 'text-opacity-disabled italic',
|
activeEnvironment == null && 'text-opacity-disabled italic',
|
||||||
)}
|
)}
|
||||||
|
{...buttonProps}
|
||||||
>
|
>
|
||||||
{activeEnvironment?.name ?? 'No Environment'}
|
{activeEnvironment?.name ?? 'No Environment'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useWindowSize } from 'react-use';
|
||||||
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
||||||
|
import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment';
|
||||||
import { useEnvironments } from '../hooks/useEnvironments';
|
import { useEnvironments } from '../hooks/useEnvironments';
|
||||||
|
import { usePrompt } from '../hooks/usePrompt';
|
||||||
|
import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment';
|
||||||
|
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
||||||
import type { Environment, Workspace } from '../lib/models';
|
import type { Environment, Workspace } from '../lib/models';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { PairEditor } from './core/PairEditor';
|
|
||||||
import type { PairEditorProps } from './core/PairEditor';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment';
|
|
||||||
import { HStack, VStack } from './core/Stacks';
|
|
||||||
import { IconButton } from './core/IconButton';
|
|
||||||
import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment';
|
|
||||||
import type { DropdownItem } from './core/Dropdown';
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
import { Icon } from './core/Icon';
|
|
||||||
import { usePrompt } from '../hooks/usePrompt';
|
|
||||||
import { InlineCode } from './core/InlineCode';
|
|
||||||
import { useWindowSize } from 'react-use';
|
|
||||||
import type {
|
import type {
|
||||||
GenericCompletionConfig,
|
GenericCompletionConfig,
|
||||||
GenericCompletionOption,
|
GenericCompletionOption,
|
||||||
} from './core/Editor/genericCompletion';
|
} from './core/Editor/genericCompletion';
|
||||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
import { Icon } from './core/Icon';
|
||||||
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
import { IconButton } from './core/IconButton';
|
||||||
|
import { InlineCode } from './core/InlineCode';
|
||||||
|
import type { PairEditorProps } from './core/PairEditor';
|
||||||
|
import { PairEditor } from './core/PairEditor';
|
||||||
|
import { HStack, VStack } from './core/Stacks';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
initialEnvironment: Environment | null;
|
initialEnvironment: Environment | null;
|
||||||
@@ -54,7 +54,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
|||||||
<aside className="grid grid-rows-[minmax(0,1fr)_auto] gap-y-0.5 h-full max-w-[250px] pr-3 border-r border-gray-100 -ml-2">
|
<aside className="grid grid-rows-[minmax(0,1fr)_auto] gap-y-0.5 h-full max-w-[250px] pr-3 border-r border-gray-100 -ml-2">
|
||||||
<div className="min-w-0 h-full w-full overflow-y-scroll">
|
<div className="min-w-0 h-full w-full overflow-y-scroll">
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
active={selectedEnvironmentId == null}
|
active={selectedEnvironment == null}
|
||||||
onClick={() => setSelectedEnvironmentId(null)}
|
onClick={() => setSelectedEnvironmentId(null)}
|
||||||
>
|
>
|
||||||
Base Environment
|
Base Environment
|
||||||
@@ -63,7 +63,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
|||||||
{environments.map((e) => (
|
{environments.map((e) => (
|
||||||
<SidebarButton
|
<SidebarButton
|
||||||
key={e.id}
|
key={e.id}
|
||||||
active={selectedEnvironmentId === e.id}
|
active={selectedEnvironment?.id === e.id}
|
||||||
onClick={() => setSelectedEnvironmentId(e.id)}
|
onClick={() => setSelectedEnvironmentId(e.id)}
|
||||||
>
|
>
|
||||||
{e.name}
|
{e.name}
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ import { useActiveWorkspaceId } from '../hooks/useActiveWorkspaceId';
|
|||||||
import { useAppRoutes } from '../hooks/useAppRoutes';
|
import { useAppRoutes } from '../hooks/useAppRoutes';
|
||||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||||
import { useRequests } from '../hooks/useRequests';
|
import { useRequests } from '../hooks/useRequests';
|
||||||
|
import type { ButtonProps } from './core/Button';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import type { DropdownItem, DropdownRef } from './core/Dropdown';
|
import type { DropdownItem, DropdownRef } from './core/Dropdown';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
|
|
||||||
export function RecentRequestsDropdown() {
|
export function RecentRequestsDropdown({ className }: Pick<ButtonProps, 'className'>) {
|
||||||
const dropdownRef = useRef<DropdownRef>(null);
|
const dropdownRef = useRef<DropdownRef>(null);
|
||||||
const activeRequest = useActiveRequest();
|
const activeRequest = useActiveRequest();
|
||||||
const activeWorkspaceId = useActiveWorkspaceId();
|
const activeWorkspaceId = useActiveWorkspaceId();
|
||||||
@@ -75,7 +76,12 @@ export function RecentRequestsDropdown() {
|
|||||||
|
|
||||||
// No recent requests to show
|
// No recent requests to show
|
||||||
if (recentRequestItems.length === 0) {
|
if (recentRequestItems.length === 0) {
|
||||||
return [];
|
return [
|
||||||
|
{
|
||||||
|
label: 'No recent requests',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
] as DropdownItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
return recentRequestItems.slice(0, 20);
|
return recentRequestItems.slice(0, 20);
|
||||||
@@ -87,7 +93,8 @@ export function RecentRequestsDropdown() {
|
|||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
size="sm"
|
size="sm"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'flex-[2] text-center text-gray-800 text-sm truncate pointer-events-auto',
|
className,
|
||||||
|
'text-gray-800 text-sm truncate pointer-events-auto',
|
||||||
activeRequest === null && 'text-opacity-disabled italic',
|
activeRequest === null && 'text-opacity-disabled italic',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,20 +1,11 @@
|
|||||||
import { invoke } from '@tauri-apps/api';
|
import { useRef } from 'react';
|
||||||
import { open } from '@tauri-apps/api/dialog';
|
|
||||||
import { useCallback, useRef } from 'react';
|
|
||||||
import { useAppRoutes } from '../hooks/useAppRoutes';
|
|
||||||
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
import { useDeleteRequest } from '../hooks/useDeleteRequest';
|
||||||
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
import { useDuplicateRequest } from '../hooks/useDuplicateRequest';
|
||||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||||
import { useTheme } from '../hooks/useTheme';
|
import type { DropdownProps, DropdownRef } from './core/Dropdown';
|
||||||
import type { Environment, Folder, HttpRequest, Workspace } from '../lib/models';
|
|
||||||
import { pluralize } from '../lib/pluralize';
|
|
||||||
import { Button } from './core/Button';
|
|
||||||
import type { DropdownItem, DropdownProps, DropdownRef } from './core/Dropdown';
|
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
import { HotKey } from './core/HotKey';
|
import { HotKey } from './core/HotKey';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import { VStack } from './core/Stacks';
|
|
||||||
import { useDialog } from './DialogContext';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
requestId: string | null;
|
requestId: string | null;
|
||||||
@@ -25,9 +16,6 @@ export function RequestActionsDropdown({ requestId, children }: Props) {
|
|||||||
const deleteRequest = useDeleteRequest(requestId);
|
const deleteRequest = useDeleteRequest(requestId);
|
||||||
const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
|
const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
|
||||||
const dropdownRef = useRef<DropdownRef>(null);
|
const dropdownRef = useRef<DropdownRef>(null);
|
||||||
const routes = useAppRoutes();
|
|
||||||
const dialog = useDialog();
|
|
||||||
const { appearance, toggleAppearance } = useTheme();
|
|
||||||
|
|
||||||
useListenToTauriEvent('toggle_settings', () => {
|
useListenToTauriEvent('toggle_settings', () => {
|
||||||
dropdownRef.current?.toggle();
|
dropdownRef.current?.toggle();
|
||||||
@@ -38,102 +26,29 @@ export function RequestActionsDropdown({ requestId, children }: Props) {
|
|||||||
duplicateRequest.mutate();
|
duplicateRequest.mutate();
|
||||||
});
|
});
|
||||||
|
|
||||||
const importData = useCallback(async () => {
|
if (requestId == null) {
|
||||||
const selected = await open({
|
return null;
|
||||||
multiple: true,
|
}
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
name: 'Export File',
|
|
||||||
extensions: ['json'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (selected == null || selected.length === 0) return;
|
|
||||||
const imported: {
|
|
||||||
workspaces: Workspace[];
|
|
||||||
environments: Environment[];
|
|
||||||
folders: Folder[];
|
|
||||||
requests: HttpRequest[];
|
|
||||||
} = await invoke('import_data', {
|
|
||||||
filePaths: selected,
|
|
||||||
});
|
|
||||||
const importedWorkspace = imported.workspaces[0];
|
|
||||||
|
|
||||||
dialog.show({
|
|
||||||
title: 'Import Complete',
|
|
||||||
size: 'dynamic',
|
|
||||||
hideX: true,
|
|
||||||
render: ({ hide }) => {
|
|
||||||
const { workspaces, environments, folders, requests } = imported;
|
|
||||||
return (
|
|
||||||
<VStack space={3}>
|
|
||||||
<ul className="list-disc pl-6">
|
|
||||||
<li>
|
|
||||||
{workspaces.length} {pluralize('Workspace', workspaces.length)}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{environments.length} {pluralize('Environment', environments.length)}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{folders.length} {pluralize('Folder', folders.length)}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
{requests.length} {pluralize('Request', requests.length)}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div>
|
|
||||||
<Button className="ml-auto" onClick={hide} color="primary">
|
|
||||||
Done
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</VStack>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (importedWorkspace != null) {
|
|
||||||
routes.navigate('workspace', {
|
|
||||||
workspaceId: importedWorkspace.id,
|
|
||||||
environmentId: imported.environments[0]?.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [routes, dialog]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
items={[
|
items={[
|
||||||
...(requestId != null
|
|
||||||
? ([
|
|
||||||
{
|
|
||||||
key: 'duplicate',
|
|
||||||
label: 'Duplicate',
|
|
||||||
onSelect: duplicateRequest.mutate,
|
|
||||||
leftSlot: <Icon icon="copy" />,
|
|
||||||
rightSlot: <HotKey modifier="Meta" keyName="D" />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
label: 'Delete',
|
|
||||||
onSelect: deleteRequest.mutate,
|
|
||||||
variant: 'danger',
|
|
||||||
leftSlot: <Icon icon="trash" />,
|
|
||||||
},
|
|
||||||
{ type: 'separator', label: 'Yaak Settings' },
|
|
||||||
] as DropdownItem[])
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
key: 'import',
|
key: 'duplicate',
|
||||||
label: 'Import',
|
label: 'Duplicate',
|
||||||
onSelect: importData,
|
onSelect: duplicateRequest.mutate,
|
||||||
leftSlot: <Icon icon="download" />,
|
leftSlot: <Icon icon="copy" />,
|
||||||
|
rightSlot: <HotKey modifier="Meta" keyName="D" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'appearance',
|
key: 'delete',
|
||||||
label: appearance === 'dark' ? 'Light Theme' : 'Dark Theme',
|
label: 'Delete',
|
||||||
onSelect: toggleAppearance,
|
onSelect: deleteRequest.mutate,
|
||||||
leftSlot: <Icon icon={appearance === 'dark' ? 'sun' : 'moon'} />,
|
variant: 'danger',
|
||||||
|
leftSlot: <Icon icon="trash" />,
|
||||||
},
|
},
|
||||||
|
{ type: 'separator', label: 'Yaak Settings' },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,24 +1,28 @@
|
|||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
|
import { open } from '@tauri-apps/api/dialog';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
import { useAppRoutes } from '../hooks/useAppRoutes';
|
import { useAppRoutes } from '../hooks/useAppRoutes';
|
||||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||||
import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace';
|
import { useDeleteWorkspace } from '../hooks/useDeleteWorkspace';
|
||||||
import { usePrompt } from '../hooks/usePrompt';
|
import { usePrompt } from '../hooks/usePrompt';
|
||||||
|
import { getRecentEnvironments } from '../hooks/useRecentEnvironments';
|
||||||
|
import { useTheme } from '../hooks/useTheme';
|
||||||
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
|
||||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||||
import { Button } from './core/Button';
|
import type { Environment, Folder, HttpRequest, Workspace } from '../lib/models';
|
||||||
|
import { pluralize } from '../lib/pluralize';
|
||||||
import type { ButtonProps } from './core/Button';
|
import type { ButtonProps } from './core/Button';
|
||||||
|
import { Button } from './core/Button';
|
||||||
import type { DropdownItem } from './core/Dropdown';
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
import { Dropdown } from './core/Dropdown';
|
import { Dropdown } from './core/Dropdown';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import { InlineCode } from './core/InlineCode';
|
import { InlineCode } from './core/InlineCode';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack, VStack } from './core/Stacks';
|
||||||
import { useDialog } from './DialogContext';
|
import { useDialog } from './DialogContext';
|
||||||
import { getRecentEnvironments } from '../hooks/useRecentEnvironments';
|
|
||||||
|
|
||||||
type Props = Pick<ButtonProps, 'className' | 'justify' | 'forDropdown'>;
|
type Props = Pick<ButtonProps, 'className' | 'justify' | 'forDropdown' | 'leftSlot'>;
|
||||||
|
|
||||||
export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
||||||
className,
|
className,
|
||||||
@@ -30,10 +34,72 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
const createWorkspace = useCreateWorkspace({ navigateAfter: true });
|
const createWorkspace = useCreateWorkspace({ navigateAfter: true });
|
||||||
const updateWorkspace = useUpdateWorkspace(activeWorkspaceId);
|
const updateWorkspace = useUpdateWorkspace(activeWorkspaceId);
|
||||||
const deleteWorkspace = useDeleteWorkspace(activeWorkspace);
|
const deleteWorkspace = useDeleteWorkspace(activeWorkspace);
|
||||||
|
const { appearance, toggleAppearance } = useTheme();
|
||||||
const dialog = useDialog();
|
const dialog = useDialog();
|
||||||
const prompt = usePrompt();
|
const prompt = usePrompt();
|
||||||
const routes = useAppRoutes();
|
const routes = useAppRoutes();
|
||||||
|
|
||||||
|
const importData = useCallback(async () => {
|
||||||
|
const selected = await open({
|
||||||
|
multiple: true,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: 'Export File',
|
||||||
|
extensions: ['json'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (selected == null || selected.length === 0) return;
|
||||||
|
const imported: {
|
||||||
|
workspaces: Workspace[];
|
||||||
|
environments: Environment[];
|
||||||
|
folders: Folder[];
|
||||||
|
requests: HttpRequest[];
|
||||||
|
} = await invoke('import_data', {
|
||||||
|
filePaths: selected,
|
||||||
|
});
|
||||||
|
const importedWorkspace = imported.workspaces[0];
|
||||||
|
|
||||||
|
dialog.show({
|
||||||
|
title: 'Import Complete',
|
||||||
|
size: 'dynamic',
|
||||||
|
hideX: true,
|
||||||
|
render: ({ hide }) => {
|
||||||
|
const { workspaces, environments, folders, requests } = imported;
|
||||||
|
return (
|
||||||
|
<VStack space={3}>
|
||||||
|
<ul className="list-disc pl-6">
|
||||||
|
<li>
|
||||||
|
{workspaces.length} {pluralize('Workspace', workspaces.length)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{environments.length} {pluralize('Environment', environments.length)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{folders.length} {pluralize('Folder', folders.length)}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{requests.length} {pluralize('Request', requests.length)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
<Button className="ml-auto" onClick={hide} color="primary">
|
||||||
|
Done
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (importedWorkspace != null) {
|
||||||
|
routes.navigate('workspace', {
|
||||||
|
workspaceId: importedWorkspace.id,
|
||||||
|
environmentId: imported.environments[0]?.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [routes, dialog]);
|
||||||
|
|
||||||
const items: DropdownItem[] = useMemo(() => {
|
const items: DropdownItem[] = useMemo(() => {
|
||||||
const workspaceItems: DropdownItem[] = workspaces.map((w) => ({
|
const workspaceItems: DropdownItem[] = workspaces.map((w) => ({
|
||||||
key: w.id,
|
key: w.id,
|
||||||
@@ -51,7 +117,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
),
|
),
|
||||||
render: ({ hide }) => {
|
render: ({ hide }) => {
|
||||||
return (
|
return (
|
||||||
<HStack space={2} justifyContent="end" className="mt-6">
|
<HStack space={2} justifyContent="end" alignItems="center" className="mt-6">
|
||||||
<Button
|
<Button
|
||||||
className="focus"
|
className="focus"
|
||||||
color="gray"
|
color="gray"
|
||||||
@@ -139,15 +205,30 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
createWorkspace.mutate({ name });
|
createWorkspace.mutate({ name });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'import',
|
||||||
|
label: 'Import Data',
|
||||||
|
onSelect: importData,
|
||||||
|
leftSlot: <Icon icon="download" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'appearance',
|
||||||
|
label: 'Toggle Theme',
|
||||||
|
onSelect: toggleAppearance,
|
||||||
|
leftSlot: <Icon icon={appearance === 'dark' ? 'sun' : 'moon'} />,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}, [
|
}, [
|
||||||
activeWorkspace?.name,
|
activeWorkspace?.name,
|
||||||
activeWorkspaceId,
|
activeWorkspaceId,
|
||||||
|
appearance,
|
||||||
createWorkspace,
|
createWorkspace,
|
||||||
deleteWorkspace.mutate,
|
deleteWorkspace.mutate,
|
||||||
dialog,
|
dialog,
|
||||||
|
importData,
|
||||||
prompt,
|
prompt,
|
||||||
routes,
|
routes,
|
||||||
|
toggleAppearance,
|
||||||
updateWorkspace,
|
updateWorkspace,
|
||||||
workspaces,
|
workspaces,
|
||||||
]);
|
]);
|
||||||
@@ -155,7 +236,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
return (
|
return (
|
||||||
<Dropdown items={items}>
|
<Dropdown items={items}>
|
||||||
<Button
|
<Button
|
||||||
forDropdown
|
|
||||||
size="sm"
|
size="sm"
|
||||||
className={classNames(className, 'text-gray-800 !px-2 truncate')}
|
className={classNames(className, 'text-gray-800 !px-2 truncate')}
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
|
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack } from './core/Stacks';
|
||||||
|
import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown';
|
||||||
import { RecentRequestsDropdown } from './RecentRequestsDropdown';
|
import { RecentRequestsDropdown } from './RecentRequestsDropdown';
|
||||||
import { RequestActionsDropdown } from './RequestActionsDropdown';
|
import { RequestActionsDropdown } from './RequestActionsDropdown';
|
||||||
import { SidebarActions } from './SidebarActions';
|
import { SidebarActions } from './SidebarActions';
|
||||||
import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown';
|
|
||||||
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -15,6 +17,7 @@ interface Props {
|
|||||||
|
|
||||||
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Props) {
|
||||||
const activeRequest = useActiveRequest();
|
const activeRequest = useActiveRequest();
|
||||||
|
const activeWorkspace = useActiveWorkspace();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
@@ -24,8 +27,17 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
|||||||
>
|
>
|
||||||
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
|
<HStack space={0.5} className="flex-1 pointer-events-none" alignItems="center">
|
||||||
<SidebarActions />
|
<SidebarActions />
|
||||||
<WorkspaceActionsDropdown />
|
<HStack alignItems="center">
|
||||||
<EnvironmentActionsDropdown className="pointer-events-auto" />
|
<WorkspaceActionsDropdown
|
||||||
|
leftSlot={
|
||||||
|
<div className="w-5 h-5 leading-5 rounded-sm text-[0.8em] bg-[#1B88DE] bg-opacity-80 text-white mr-1">
|
||||||
|
{activeWorkspace?.name[0]?.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Icon icon="chevronRight" className="text-gray-900 text-opacity-disabled" />
|
||||||
|
<EnvironmentActionsDropdown className="w-auto pointer-events-auto" />
|
||||||
|
</HStack>
|
||||||
</HStack>
|
</HStack>
|
||||||
<div className="pointer-events-none">
|
<div className="pointer-events-none">
|
||||||
<RecentRequestsDropdown />
|
<RecentRequestsDropdown />
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const _Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|||||||
() =>
|
() =>
|
||||||
classNames(
|
classNames(
|
||||||
className,
|
className,
|
||||||
|
'max-w-full min-w-0', // Help with truncation
|
||||||
'whitespace-nowrap outline-none',
|
'whitespace-nowrap outline-none',
|
||||||
'flex-shrink-0 flex items-center',
|
'flex-shrink-0 flex items-center',
|
||||||
'focus-visible-or-class:ring rounded-md',
|
'focus-visible-or-class:ring rounded-md',
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown
|
|||||||
|
|
||||||
const windowSize = useWindowSize();
|
const windowSize = useWindowSize();
|
||||||
const triggerRect = useMemo(() => {
|
const triggerRect = useMemo(() => {
|
||||||
windowSize; // Make TS happy with this dep
|
if (!windowSize) return null; // No-op to TS happy with this dep
|
||||||
if (!open) return null;
|
if (!open) return null;
|
||||||
return buttonRef.current?.getBoundingClientRect();
|
return buttonRef.current?.getBoundingClientRect();
|
||||||
}, [open, windowSize]);
|
}, [open, windowSize]);
|
||||||
@@ -368,6 +368,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onMouseEnter={(e) => e.currentTarget.focus()}
|
onMouseEnter={(e) => e.currentTarget.focus()}
|
||||||
onMouseLeave={(e) => e.currentTarget.blur()}
|
onMouseLeave={(e) => e.currentTarget.blur()}
|
||||||
|
disabled={item.disabled}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
justify="start"
|
justify="start"
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
@apply cursor-text;
|
@apply cursor-text;
|
||||||
|
@apply caret-transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cm-cursor {
|
||||||
|
@apply border-gray-800 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.cm-focused {
|
&.cm-focused {
|
||||||
@@ -17,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cm-line {
|
.cm-line {
|
||||||
@apply text-gray-800 caret-gray-800 pl-1 pr-1.5;
|
@apply text-gray-800 pl-1 pr-1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cm-placeholder {
|
.cm-placeholder {
|
||||||
@@ -159,7 +164,7 @@
|
|||||||
@apply h-full flex items-center;
|
@apply h-full flex items-center;
|
||||||
|
|
||||||
/* Break characters on line wrapping mode, useful for URL field.
|
/* Break characters on line wrapping mode, useful for URL field.
|
||||||
* We can make this dynamic if we need it to be configurable later
|
* We can make this dynamic if we need it to be configurable later
|
||||||
*/
|
*/
|
||||||
&.cm-lineWrapping {
|
&.cm-lineWrapping {
|
||||||
@apply break-all;
|
@apply break-all;
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ module.exports = {
|
|||||||
'5xl': '3.052rem',
|
'5xl': '3.052rem',
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
selection: 'hsl(var(--color-violet-500) / 0.4)',
|
selection: 'hsl(var(--color-violet-500) / 0.3)',
|
||||||
focus: 'hsl(var(--color-blue-500) / 0.6)',
|
focus: 'hsl(var(--color-blue-500) / 0.6)',
|
||||||
invalid: 'hsl(var(--color-red-500))',
|
invalid: 'hsl(var(--color-red-500))',
|
||||||
highlight: 'hsl(var(--color-gray-300) / 0.35)',
|
highlight: 'hsl(var(--color-gray-300) / 0.35)',
|
||||||
|
|||||||
Reference in New Issue
Block a user