chore: refactor sheet components

This commit is contained in:
Johnny
2025-12-22 09:19:12 +08:00
parent e62eebf906
commit ac4336b6c3
4 changed files with 380 additions and 383 deletions
@@ -7,7 +7,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Sheet, SheetBody, SheetContent, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import useLoading from "@/hooks/useLoading";
import { useCollectionStore, useShortcutStore, useWorkspaceStore } from "@/stores";
import { Collection } from "@/types/proto/api/v1/collection_service";
@@ -163,107 +163,108 @@ const CreateCollectionDrawer: React.FC<Props> = (props: Props) => {
return (
<Sheet open={true} onOpenChange={onClose}>
<SheetContent className="w-full sm:max-w-md overflow-y-auto">
<SheetContent className="w-full sm:max-w-md">
<SheetHeader>
<SheetTitle>{isCreating ? "Create Collection" : "Edit Collection"}</SheetTitle>
<SheetDescription>{isCreating ? "Create a new collection of shortcuts" : "Edit your collection details"}</SheetDescription>
</SheetHeader>
<div className="mt-6 space-y-4">
<div className="space-y-2">
<Label htmlFor="name">
Name <span className="text-destructive">*</span>
</Label>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">c/</span>
<SheetBody>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">
Name <span className="text-destructive">*</span>
</Label>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">c/</span>
<Input
id="name"
type="text"
placeholder="An easy name to remember"
value={state.collectionCreate.name}
onChange={handleNameInputChange}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="title">
Title <span className="text-destructive">*</span>
</Label>
<Input
id="name"
id="title"
type="text"
placeholder="An easy name to remember"
value={state.collectionCreate.name}
onChange={handleNameInputChange}
placeholder="A short title of your collection"
value={state.collectionCreate.title}
onChange={handleTitleInputChange}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="title">
Title <span className="text-destructive">*</span>
</Label>
<Input
id="title"
type="text"
placeholder="A short title of your collection"
value={state.collectionCreate.title}
onChange={handleTitleInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Input
id="description"
type="text"
placeholder="A slightly longer description"
value={state.collectionCreate.description}
onChange={handleDescriptionInputChange}
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="public"
checked={state.collectionCreate.visibility === Visibility.PUBLIC}
onCheckedChange={(checked) =>
setPartialState({
collectionCreate: Object.assign(state.collectionCreate, {
visibility: checked ? Visibility.PUBLIC : Visibility.WORKSPACE,
}),
})
}
/>
<Label htmlFor="public" className="text-sm font-normal cursor-pointer">
{t(`shortcut.visibility.public.description`)}
</Label>
</div>
<Separator />
<div className="space-y-2">
<div className="flex items-baseline gap-2">
<Label>Shortcuts</Label>
<span className="text-sm text-muted-foreground">({selectedShortcuts.length})</span>
{selectedShortcuts.length === 0 && <span className="text-sm italic text-muted-foreground">(Select a shortcut first)</span>}
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Input
id="description"
type="text"
placeholder="A slightly longer description"
value={state.collectionCreate.description}
onChange={handleDescriptionInputChange}
/>
</div>
<div className="w-full py-1 px-px flex flex-row justify-start items-start flex-wrap gap-2">
{selectedShortcuts.map((shortcut) => {
return (
<ShortcutView
key={shortcut.id}
className="!w-auto select-none max-w-[40%] cursor-pointer bg-muted shadow"
shortcut={shortcut}
onClick={() => {
setSelectedShortcuts([...selectedShortcuts.filter((selectedShortcut) => selectedShortcut.id !== shortcut.id)]);
}}
/>
);
})}
{unselectedShortcuts.map((shortcut) => {
return (
<ShortcutView
key={shortcut.id}
className="!w-auto select-none max-w-[40%] border-dashed cursor-pointer"
shortcut={shortcut}
onClick={() => {
setSelectedShortcuts([...selectedShortcuts, shortcut]);
}}
/>
);
})}
{selectedShortcuts.length + unselectedShortcuts.length === 0 && (
<div className="w-full flex flex-row justify-center items-center text-muted-foreground">
<Icon.PackageOpen className="w-6 h-auto" />
<p className="ml-2">No shortcuts found.</p>
</div>
)}
<div className="flex items-center space-x-2">
<Checkbox
id="public"
checked={state.collectionCreate.visibility === Visibility.PUBLIC}
onCheckedChange={(checked) =>
setPartialState({
collectionCreate: Object.assign(state.collectionCreate, {
visibility: checked ? Visibility.PUBLIC : Visibility.WORKSPACE,
}),
})
}
/>
<Label htmlFor="public" className="text-sm font-normal cursor-pointer">
{t(`shortcut.visibility.public.description`)}
</Label>
</div>
<Separator />
<div className="space-y-2">
<div className="flex items-baseline gap-2">
<Label>Shortcuts</Label>
<span className="text-sm text-muted-foreground">({selectedShortcuts.length})</span>
{selectedShortcuts.length === 0 && <span className="text-sm italic text-muted-foreground">(Select a shortcut first)</span>}
</div>
<div className="w-full py-1 px-px flex flex-row justify-start items-start flex-wrap gap-2">
{selectedShortcuts.map((shortcut) => {
return (
<ShortcutView
key={shortcut.id}
className="!w-auto select-none max-w-[40%] cursor-pointer bg-muted shadow"
shortcut={shortcut}
onClick={() => {
setSelectedShortcuts([...selectedShortcuts.filter((selectedShortcut) => selectedShortcut.id !== shortcut.id)]);
}}
/>
);
})}
{unselectedShortcuts.map((shortcut) => {
return (
<ShortcutView
key={shortcut.id}
className="!w-auto select-none max-w-[40%] border-dashed cursor-pointer"
shortcut={shortcut}
onClick={() => {
setSelectedShortcuts([...selectedShortcuts, shortcut]);
}}
/>
);
})}
{selectedShortcuts.length + unselectedShortcuts.length === 0 && (
<div className="w-full flex flex-row justify-center items-center text-muted-foreground">
<Icon.PackageOpen className="w-6 h-auto" />
<p className="ml-2">No shortcuts found.</p>
</div>
)}
</div>
</div>
</div>
</div>
<SheetFooter className="mt-6">
</SheetBody>
<SheetFooter>
<Button variant="outline" disabled={requestState.isLoading} onClick={onClose}>
{t("common.cancel")}
</Button>
@@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Sheet, SheetBody, SheetContent, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { workspaceServiceClient } from "@/grpcweb";
import { absolutifyLink } from "@/helpers/utils";
import useLoading from "@/hooks/useLoading";
@@ -137,150 +137,136 @@ const CreateIdentityProviderDrawer: React.FC<Props> = (props: Props) => {
return (
<Sheet open={true} onOpenChange={onClose}>
<SheetContent className="w-full sm:max-w-md overflow-y-auto">
<SheetContent className="w-full sm:max-w-md">
<SheetHeader>
<SheetTitle>{isCreating ? "Create Identity Provider" : "Edit Identity Provider"}</SheetTitle>
<SheetDescription>
{isCreating ? "Configure a new OAuth2 identity provider" : "Edit your identity provider settings"}
</SheetDescription>
</SheetHeader>
<div className="mt-6 space-y-4">
<div className="space-y-2">
<Label htmlFor="title">
Title <span className="text-destructive">*</span>
</Label>
<Input
id="title"
type="text"
placeholder="A short title will be displayed in the UI"
value={state.identityProviderCreate.title}
onChange={handleTitleInputChange}
/>
<SheetBody>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="title">
Title <span className="text-destructive">*</span>
</Label>
<Input
id="title"
type="text"
placeholder="A short title will be displayed in the UI"
value={state.identityProviderCreate.title}
onChange={handleTitleInputChange}
/>
</div>
<Separator />
<div className="space-y-2">
<p className="text-sm font-medium">Identity provider information</p>
{isCreating && (
<div className="rounded-md py-2 px-3 bg-secondary text-sm w-full break-all">
<span className="text-muted-foreground">Redirect URL</span>
<br />
<code className="text-xs">{absolutifyLink("/auth/callback")}</code>
</div>
)}
</div>
<div className="space-y-2">
<Label htmlFor="client-id">
Client ID <span className="text-destructive">*</span>
</Label>
<Input
id="client-id"
type="text"
placeholder="Client ID of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.clientId}
onChange={(e) => handleOAuth2ConfigChange(e, "clientId")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="client-secret">
Client Secret <span className="text-destructive">*</span>
</Label>
<Input
id="client-secret"
type="text"
placeholder="Client Secret of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.clientSecret}
onChange={(e) => handleOAuth2ConfigChange(e, "clientSecret")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="auth-url">
Authorization endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="auth-url"
type="text"
placeholder="Authorization endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.authUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "authUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="token-url">
Token endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="token-url"
type="text"
placeholder="Token endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.tokenUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "tokenUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="user-info-url">
User endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="user-info-url"
type="text"
placeholder="User endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.userInfoUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "userInfoUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="scopes">
Scopes <span className="text-destructive">*</span>
</Label>
<Input
id="scopes"
type="text"
placeholder="Scopes of the OAuth2 provider, separated by space"
value={state.identityProviderCreate.config?.oauth2?.scopes.join(" ")}
onChange={(e) => handleOAuth2ConfigChange(e, "scopes")}
/>
</div>
<Separator />
<div className="space-y-2">
<p className="text-sm font-medium">Field mapping</p>
</div>
<div className="space-y-2">
<Label htmlFor="identifier">
Identifier <span className="text-destructive">*</span>
</Label>
<Input
id="identifier"
type="text"
placeholder="The field in the user info response to identify the user"
value={state.identityProviderCreate.config?.oauth2?.fieldMapping?.identifier}
onChange={(e) => handleFieldMappingChange(e, "identifier")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="display-name">Display name</Label>
<Input
id="display-name"
type="text"
placeholder="The field in the user info response to display the user"
value={state.identityProviderCreate.config?.oauth2?.fieldMapping?.displayName}
onChange={(e) => handleFieldMappingChange(e, "displayName")}
/>
</div>
</div>
<Separator />
<div className="space-y-2">
<p className="text-sm font-medium">Identity provider information</p>
{isCreating && (
<div className="rounded-md py-2 px-3 bg-secondary text-sm w-full break-all">
<span className="text-muted-foreground">Redirect URL</span>
<br />
<code className="text-xs">{absolutifyLink("/auth/callback")}</code>
</div>
)}
</div>
<div className="space-y-2">
<Label htmlFor="client-id">
Client ID <span className="text-destructive">*</span>
</Label>
<Input
id="client-id"
type="text"
placeholder="Client ID of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.clientId}
onChange={(e) => handleOAuth2ConfigChange(e, "clientId")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="client-secret">
Client Secret <span className="text-destructive">*</span>
</Label>
<Input
id="client-secret"
type="text"
placeholder="Client Secret of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.clientSecret}
onChange={(e) => handleOAuth2ConfigChange(e, "clientSecret")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="auth-url">
Authorization endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="auth-url"
type="text"
placeholder="Authorization endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.authUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "authUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="token-url">
Token endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="token-url"
type="text"
placeholder="Token endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.tokenUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "tokenUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="user-info-url">
User endpoint <span className="text-destructive">*</span>
</Label>
<Input
id="user-info-url"
type="text"
placeholder="User endpoint of the OAuth2 provider"
value={state.identityProviderCreate.config?.oauth2?.userInfoUrl}
onChange={(e) => handleOAuth2ConfigChange(e, "userInfoUrl")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="scopes">
Scopes <span className="text-destructive">*</span>
</Label>
<Input
id="scopes"
type="text"
placeholder="Scopes of the OAuth2 provider, separated by space"
value={state.identityProviderCreate.config?.oauth2?.scopes.join(" ")}
onChange={(e) => handleOAuth2ConfigChange(e, "scopes")}
/>
</div>
<Separator />
<div className="space-y-2">
<p className="text-sm font-medium">Field mapping</p>
</div>
<div className="space-y-2">
<Label htmlFor="identifier">
Identifier <span className="text-destructive">*</span>
</Label>
<Input
id="identifier"
type="text"
placeholder="The field in the user info response to identify the user"
value={state.identityProviderCreate.config?.oauth2?.fieldMapping?.identifier}
onChange={(e) => handleFieldMappingChange(e, "identifier")}
/>
</div>
<div className="space-y-2">
<Label htmlFor="display-name">Display name</Label>
<Input
id="display-name"
type="text"
placeholder="The field in the user info response to display the user"
value={state.identityProviderCreate.config?.oauth2?.fieldMapping?.displayName}
onChange={(e) => handleFieldMappingChange(e, "displayName")}
/>
</div>
</div>
<SheetFooter className="mt-6">
</SheetBody>
<SheetFooter>
<Button variant="outline" disabled={requestState.isLoading} onClick={onClose}>
{t("common.cancel")}
</Button>
@@ -8,7 +8,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Sheet, SheetBody, SheetContent, SheetFooter, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { Textarea } from "@/components/ui/textarea";
import useLoading from "@/hooks/useLoading";
import { useShortcutStore, useWorkspaceStore } from "@/stores";
@@ -211,163 +211,156 @@ const CreateShortcutDrawer: React.FC<Props> = (props: Props) => {
return (
<Sheet open={true} onOpenChange={onClose}>
<SheetContent className="w-full sm:max-w-md overflow-y-auto">
<SheetContent className="w-full sm:max-w-md">
<SheetHeader>
<SheetTitle>{isCreating ? "Create Shortcut" : "Edit Shortcut"}</SheetTitle>
<SheetDescription>{isCreating ? "Create a new shortcut" : "Edit your shortcut details"}</SheetDescription>
</SheetHeader>
<div className="mt-6 space-y-4">
<div className="space-y-2">
<Label htmlFor="name">
Name <span className="text-destructive">*</span>
</Label>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">s/</span>
<SheetBody>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">
Name <span className="text-destructive">*</span>
</Label>
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">s/</span>
<Input
id="name"
type="text"
placeholder="An easy name to remember"
value={state.shortcutCreate.name}
onChange={handleNameInputChange}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="link">
Link <span className="text-destructive">*</span>
</Label>
<Input
id="name"
id="link"
type="text"
placeholder="An easy name to remember"
value={state.shortcutCreate.name}
onChange={handleNameInputChange}
placeholder="The destination link of the shortcut"
value={state.shortcutCreate.link}
onChange={handleLinkInputChange}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="link">
Link <span className="text-destructive">*</span>
</Label>
<Input
id="link"
type="text"
placeholder="The destination link of the shortcut"
value={state.shortcutCreate.link}
onChange={handleLinkInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="title">Title</Label>
<Input
id="title"
type="text"
placeholder="The title of the shortcut"
value={state.shortcutCreate.title}
onChange={handleTitleInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Input
id="description"
type="text"
placeholder="A short description of the shortcut"
value={state.shortcutCreate.description}
onChange={handleDescriptionInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="tags">Tags</Label>
<Input id="tags" type="text" placeholder="The tags of shortcut" value={tag} onChange={handleTagsInputChange} />
{tagSuggestions.length > 0 && (
<div className="flex flex-row items-start gap-2 mt-2">
<Icon.Asterisk className="w-4 h-auto shrink-0 mt-0.5 text-muted-foreground" />
<div className="flex flex-row flex-wrap gap-2">
{tagSuggestions.map((tag) => (
<span
className="text-muted-foreground cursor-pointer text-sm hover:text-foreground transition-colors"
key={tag}
onClick={() => handleTagSuggestionsClick(tag)}
>
{tag}
</span>
))}
<div className="space-y-2">
<Label htmlFor="title">Title</Label>
<Input
id="title"
type="text"
placeholder="The title of the shortcut"
value={state.shortcutCreate.title}
onChange={handleTitleInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Input
id="description"
type="text"
placeholder="A short description of the shortcut"
value={state.shortcutCreate.description}
onChange={handleDescriptionInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="tags">Tags</Label>
<Input id="tags" type="text" placeholder="The tags of shortcut" value={tag} onChange={handleTagsInputChange} />
{tagSuggestions.length > 0 && (
<div className="flex flex-row items-start gap-2 mt-2">
<Icon.Asterisk className="w-4 h-auto shrink-0 mt-0.5 text-muted-foreground" />
<div className="flex flex-row flex-wrap gap-2">
{tagSuggestions.map((tag) => (
<span
className="text-muted-foreground cursor-pointer text-sm hover:text-foreground transition-colors"
key={tag}
onClick={() => handleTagSuggestionsClick(tag)}
>
{tag}
</span>
))}
</div>
</div>
</div>
)}
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="public"
checked={state.shortcutCreate.visibility === Visibility.PUBLIC}
onCheckedChange={(checked) =>
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
visibility: checked ? Visibility.PUBLIC : Visibility.WORKSPACE,
}),
})
}
/>
<Label htmlFor="public" className="text-sm font-normal cursor-pointer">
{t(`shortcut.visibility.public.description`)}
</Label>
</div>
<Separator className="my-4" />
<div className="border rounded-lg overflow-hidden">
<div
className={classnames(
"flex flex-row justify-between items-center px-3 py-2 cursor-pointer hover:bg-accent transition-colors",
showOpenGraphMetadata && "bg-accent border-b",
)}
onClick={() => setShowOpenGraphMetadata(!showOpenGraphMetadata)}
>
<span className="text-sm flex items-center gap-1">
Social media metadata
<Icon.Sparkles className="w-4 h-auto text-primary" />
</span>
<Icon.ChevronDown
className={classnames("w-4 h-auto text-muted-foreground transition-transform", showOpenGraphMetadata && "rotate-180")}
/>
</div>
{showOpenGraphMetadata && (
<div className="p-3 space-y-4">
<div className="space-y-2">
<Label htmlFor="og-image" className="text-sm">
Image URL
</Label>
<Input
id="og-image"
type="text"
placeholder="https://the.link.to/the/image.png"
value={state.shortcutCreate.ogMetadata?.image}
onChange={handleOpenGraphMetadataImageChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="og-title" className="text-sm">
Title
</Label>
<Input
id="og-title"
type="text"
placeholder="Slash - An open source, self-hosted platform"
value={state.shortcutCreate.ogMetadata?.title}
onChange={handleOpenGraphMetadataTitleChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="og-description" className="text-sm">
Description
</Label>
<Textarea
id="og-description"
placeholder="An open source, self-hosted platform for sharing and managing your most frequently used links."
rows={3}
value={state.shortcutCreate.ogMetadata?.description}
onChange={handleOpenGraphMetadataDescriptionChange}
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="public"
checked={state.shortcutCreate.visibility === Visibility.PUBLIC}
onCheckedChange={(checked) =>
setPartialState({
shortcutCreate: Object.assign(state.shortcutCreate, {
visibility: checked ? Visibility.PUBLIC : Visibility.WORKSPACE,
}),
})
}
/>
<Label htmlFor="public" className="text-sm font-normal cursor-pointer">
{t(`shortcut.visibility.public.description`)}
</Label>
</div>
<Separator className="my-4" />
<div className="border rounded-lg overflow-hidden">
<div
className={classnames(
"flex flex-row justify-between items-center px-3 py-2 cursor-pointer hover:bg-accent transition-colors",
showOpenGraphMetadata && "bg-accent border-b",
)}
onClick={() => setShowOpenGraphMetadata(!showOpenGraphMetadata)}
>
<span className="text-sm flex items-center gap-1">
Social media metadata
<Icon.Sparkles className="w-4 h-auto text-primary" />
</span>
<Icon.ChevronDown
className={classnames("w-4 h-auto text-muted-foreground transition-transform", showOpenGraphMetadata && "rotate-180")}
/>
</div>
)}
{showOpenGraphMetadata && (
<div className="p-3 space-y-4">
<div className="space-y-2">
<Label htmlFor="og-image" className="text-sm">
Image URL
</Label>
<Input
id="og-image"
type="text"
placeholder="https://the.link.to/the/image.png"
value={state.shortcutCreate.ogMetadata?.image}
onChange={handleOpenGraphMetadataImageChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="og-title" className="text-sm">
Title
</Label>
<Input
id="og-title"
type="text"
placeholder="Slash - An open source, self-hosted platform"
value={state.shortcutCreate.ogMetadata?.title}
onChange={handleOpenGraphMetadataTitleChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="og-description" className="text-sm">
Description
</Label>
<Textarea
id="og-description"
placeholder="An open source, self-hosted platform for sharing and managing your most frequently used links."
rows={3}
value={state.shortcutCreate.ogMetadata?.description}
onChange={handleOpenGraphMetadataDescriptionChange}
/>
</div>
</div>
)}
</div>
</div>
</div>
<SheetFooter className="mt-6">
</SheetBody>
<SheetFooter>
<Button variant="outline" onClick={onClose} disabled={requestState.isLoading}>
{t("common.cancel")}
</Button>
+21 -4
View File
@@ -28,7 +28,7 @@ const SheetOverlay = React.forwardRef<
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 flex flex-col overflow-hidden",
{
variants: {
side: {
@@ -64,12 +64,17 @@ const SheetContent = React.forwardRef<React.ElementRef<typeof SheetPrimitive.Con
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
<div className={cn("flex flex-col space-y-2 text-center sm:text-left flex-shrink-0", className)} {...props} />
);
SheetHeader.displayName = "SheetHeader";
const SheetBody = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex-1 overflow-y-auto min-h-0", className)} {...props} />
);
SheetBody.displayName = "SheetBody";
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4 flex-shrink-0", className)} {...props} />
);
SheetFooter.displayName = "SheetFooter";
@@ -89,4 +94,16 @@ const SheetDescription = React.forwardRef<
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
export { Sheet, SheetPortal, SheetOverlay, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetFooter, SheetTitle, SheetDescription };
export {
Sheet,
SheetPortal,
SheetOverlay,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetBody,
SheetFooter,
SheetTitle,
SheetDescription,
};