mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-11-08 04:55:06 +09:00
Merge branch 'main' into text-control-prefix-icon
This commit is contained in:
15 files changed
+327
-193
No files matched your search
-1
@@ -29,7 +29,6 @@ export const FontFamilyControl = ({
|
||||
content={<FontsManager value={String(value.value)} onChange={setValue} />}
|
||||
onOpenChange={setIsOpen}
|
||||
>
|
||||
{/* @todo this should be part of the design system, probably a varian="button" */}
|
||||
<TextField
|
||||
defaultValue={value.value}
|
||||
state={isOpen ? "active" : undefined}
|
||||
|
||||
@@ -30,13 +30,12 @@ export const ImageControl = ({
|
||||
title="Images"
|
||||
content={
|
||||
<ImageManager
|
||||
onSelect={(asset) => {
|
||||
onChange={(asset) => {
|
||||
setValue(asset.id);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{/* @todo this should be part of the design system, probably a varian="button" */}
|
||||
<TextField defaultValue={selectedAsset?.name} />
|
||||
</ValuePickerPopover>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { type ComponentProps } from "react";
|
||||
import type { AssetType } from "@webstudio-is/asset-uploader";
|
||||
import { Flex, SearchField } from "@webstudio-is/design-system";
|
||||
import { AssetUpload } from "./asset-upload";
|
||||
import { NotFound } from "./not-found";
|
||||
import { Separator } from "./separator";
|
||||
|
||||
type AssetsShellProps = {
|
||||
searchProps: ComponentProps<typeof SearchField>;
|
||||
children: JSX.Element;
|
||||
type: AssetType;
|
||||
isEmpty: boolean;
|
||||
};
|
||||
|
||||
export const AssetsShell = ({
|
||||
searchProps,
|
||||
isEmpty,
|
||||
children,
|
||||
type,
|
||||
}: AssetsShellProps) => {
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
css={{ overflow: "hidden", paddingTop: "$1", paddingBottom: "$3" }}
|
||||
>
|
||||
<Flex css={{ py: "$2", px: "$3" }} gap="2" direction="column">
|
||||
<AssetUpload type={type} />
|
||||
<SearchField {...searchProps} autoFocus placeholder="Search" />
|
||||
</Flex>
|
||||
<Separator />
|
||||
{isEmpty && <NotFound />}
|
||||
<Flex
|
||||
css={{
|
||||
flexDirection: "column",
|
||||
px: "$3",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from "./asset-upload";
|
||||
export * from "./use-assets";
|
||||
export * from "./types";
|
||||
export * from "./use-set-assets";
|
||||
export * from "./separator";
|
||||
export * from "./use-search";
|
||||
export * from "./assets-shell";
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Flex } from "@webstudio-is/design-system";
|
||||
|
||||
export const NotFound = () => {
|
||||
return (
|
||||
<Flex align="center" justify="center" css={{ height: 100 }}>
|
||||
No matching assets
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import {
|
||||
Separator as SeparatorPrimitive,
|
||||
styled,
|
||||
} from "@webstudio-is/design-system";
|
||||
|
||||
export const Separator = styled(SeparatorPrimitive, {
|
||||
marginTop: "$1",
|
||||
marginBottom: "$2",
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Asset } from "@webstudio-is/asset-uploader";
|
||||
|
||||
export type PreviewAsset = Pick<
|
||||
Asset,
|
||||
"path" | "name" | "id" | "status" | "format"
|
||||
>;
|
||||
export type PreviewAsset = Pick<Asset, "path" | "name" | "id" | "format"> & {
|
||||
status: "uploading";
|
||||
};
|
||||
|
||||
export type ActionData = {
|
||||
uploadedAssets?: Array<Asset>;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
export const useFilter = <Item>({
|
||||
items,
|
||||
onReset,
|
||||
}: {
|
||||
onReset: () => void;
|
||||
items: Array<Item>;
|
||||
}) => {
|
||||
const [filteredItems, setFilteredItems] = useState(items);
|
||||
const onResetRef = useRef(onReset);
|
||||
onResetRef.current = onReset;
|
||||
|
||||
const resetFilteredItems = useCallback(() => {
|
||||
setFilteredItems(items);
|
||||
}, [items]);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredItems(items);
|
||||
onResetRef.current();
|
||||
}, [items]);
|
||||
|
||||
return {
|
||||
filteredItems,
|
||||
resetFilteredItems,
|
||||
setFilteredItems,
|
||||
};
|
||||
};
|
||||
File renamed without changes.
@@ -1,15 +1,16 @@
|
||||
import {
|
||||
Flex,
|
||||
Separator as SeparatorPrimitive,
|
||||
SearchField,
|
||||
List,
|
||||
ListItem,
|
||||
useList,
|
||||
findNextListIndex,
|
||||
styled,
|
||||
} from "@webstudio-is/design-system";
|
||||
import { AssetUpload, useAssets } from "~/designer/shared/assets";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
AssetsShell,
|
||||
Separator,
|
||||
useAssets,
|
||||
useSearch,
|
||||
} from "~/designer/shared/assets";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useMenu } from "./item-menu";
|
||||
import { CheckIcon } from "@webstudio-is/icons";
|
||||
import {
|
||||
@@ -19,38 +20,7 @@ import {
|
||||
groupItemsByType,
|
||||
toItems,
|
||||
} from "./item-utils";
|
||||
import { useSearch } from "./use-search";
|
||||
|
||||
const NotFound = () => {
|
||||
return (
|
||||
<Flex align="center" justify="center" css={{ height: 100 }}>
|
||||
Font not found
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const useFilteredItems = ({ onReset }: { onReset: () => void }) => {
|
||||
const { assets } = useAssets("font");
|
||||
const fontItems = useMemo(() => toItems(assets), [assets]);
|
||||
const [filteredItems, setFilteredItems] = useState(fontItems);
|
||||
const onResetRef = useRef(onReset);
|
||||
onResetRef.current = onReset;
|
||||
|
||||
const resetFilteredItems = useCallback(() => {
|
||||
setFilteredItems(fontItems);
|
||||
}, [fontItems]);
|
||||
|
||||
useEffect(() => {
|
||||
setFilteredItems(fontItems);
|
||||
onResetRef.current();
|
||||
}, [fontItems]);
|
||||
|
||||
return {
|
||||
filteredItems,
|
||||
resetFilteredItems,
|
||||
setFilteredItems,
|
||||
};
|
||||
};
|
||||
import { useFilter } from "../assets/use-filter";
|
||||
|
||||
const useLogic = ({
|
||||
onChange,
|
||||
@@ -61,12 +31,13 @@ const useLogic = ({
|
||||
}) => {
|
||||
const { assets, handleDelete: handleDeleteAssets } = useAssets("font");
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
const { filteredItems, resetFilteredItems, setFilteredItems } =
|
||||
useFilteredItems({
|
||||
onReset() {
|
||||
searchProps.onCancel();
|
||||
},
|
||||
});
|
||||
const fontItems = useMemo(() => toItems(assets), [assets]);
|
||||
const { filteredItems, resetFilteredItems, setFilteredItems } = useFilter({
|
||||
items: fontItems,
|
||||
onReset() {
|
||||
searchProps.onCancel();
|
||||
},
|
||||
});
|
||||
const { uploadedItems, systemItems, groupedItems } = useMemo(
|
||||
() => groupItemsByType(filteredItems),
|
||||
[filteredItems]
|
||||
@@ -136,11 +107,6 @@ const useLogic = ({
|
||||
};
|
||||
};
|
||||
|
||||
const Separator = styled(SeparatorPrimitive, {
|
||||
marginTop: "$1",
|
||||
marginBottom: "$2",
|
||||
});
|
||||
|
||||
type FontsManagerProps = {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
@@ -180,46 +146,33 @@ export const FontsManager = ({ value, onChange }: FontsManagerProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Flex
|
||||
direction="column"
|
||||
css={{ overflow: "hidden", paddingTop: "$1", paddingBottom: "$3" }}
|
||||
<AssetsShell
|
||||
searchProps={searchProps}
|
||||
type="font"
|
||||
isEmpty={groupedItems.length === 0}
|
||||
>
|
||||
<Flex css={{ py: "$2", px: "$3" }} gap="2" direction="column">
|
||||
<AssetUpload type="font" />
|
||||
<SearchField {...searchProps} autoFocus placeholder="Search" />
|
||||
</Flex>
|
||||
<Separator />
|
||||
{groupedItems.length === 0 && <NotFound />}
|
||||
<Flex
|
||||
css={{
|
||||
flexDirection: "column",
|
||||
gap: "$3",
|
||||
px: "$3",
|
||||
<List
|
||||
{...listProps}
|
||||
onBlur={(event) => {
|
||||
if (isMenuOpen === false) {
|
||||
listProps.onBlur(event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<List
|
||||
{...listProps}
|
||||
onBlur={(event) => {
|
||||
if (isMenuOpen === false) {
|
||||
listProps.onBlur(event);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{uploadedItems.length !== 0 && (
|
||||
<ListItem state="disabled">{"Uploaded"}</ListItem>
|
||||
)}
|
||||
{uploadedItems.map(renderItem)}
|
||||
{systemItems.length !== 0 && (
|
||||
<>
|
||||
{uploadedItems.length !== 0 && <Separator />}
|
||||
<ListItem state="disabled">{"System"}</ListItem>
|
||||
</>
|
||||
)}
|
||||
{systemItems.map((item, index) =>
|
||||
renderItem(item, index + uploadedItems.length)
|
||||
)}
|
||||
</List>
|
||||
</Flex>
|
||||
</Flex>
|
||||
{uploadedItems.length !== 0 && (
|
||||
<ListItem state="disabled">{"Uploaded"}</ListItem>
|
||||
)}
|
||||
{uploadedItems.map(renderItem)}
|
||||
{systemItems.length !== 0 && (
|
||||
<>
|
||||
{uploadedItems.length !== 0 && <Separator />}
|
||||
<ListItem state="disabled">{"System"}</ListItem>
|
||||
</>
|
||||
)}
|
||||
{systemItems.map((item, index) =>
|
||||
renderItem(item, index + uploadedItems.length)
|
||||
)}
|
||||
</List>
|
||||
</AssetsShell>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Flex, Text } from "@webstudio-is/design-system";
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
type FilenameProps = Omit<ComponentProps<typeof Text>, "children"> & {
|
||||
children: string;
|
||||
};
|
||||
|
||||
export const Filename = ({ children, ...props }: FilenameProps) => {
|
||||
const splitName = children.split(".");
|
||||
const extension = splitName[splitName.length - 1];
|
||||
const baseName = children.substr(0, children.length - extension.length - 1);
|
||||
return (
|
||||
<Flex>
|
||||
<Text truncate {...props}>
|
||||
{baseName}
|
||||
</Text>
|
||||
<Text {...props}>{extension}</Text>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@@ -8,41 +8,7 @@ import {
|
||||
} from "@webstudio-is/icons";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { Asset } from "@webstudio-is/asset-uploader";
|
||||
|
||||
const Filename = ({ name }: { name: string }) => {
|
||||
const splitName = name.split(".");
|
||||
const extension = splitName[splitName.length - 1];
|
||||
const lastLetterBeforeExtension = splitName[splitName.length - 2]
|
||||
.split("")
|
||||
.pop();
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
position: "relative",
|
||||
maxWidth: "70%",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
variant="label"
|
||||
data-extension={`${lastLetterBeforeExtension}.${extension}`}
|
||||
css={{
|
||||
whiteSpace: "nowrap",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "hidden",
|
||||
"&::after": {
|
||||
content: "attr(data-extension)",
|
||||
position: "absolute",
|
||||
left: "100%",
|
||||
top: 0,
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
import { Filename } from "./filename";
|
||||
|
||||
type ImageInfoProps = {
|
||||
asset: Asset;
|
||||
@@ -56,7 +22,7 @@ export const ImageInfo = ({ asset, onDelete }: ImageInfoProps) => {
|
||||
<Box css={{ p: "$2 $3" }}>
|
||||
<Grid columns={2} align="center" gap={2}>
|
||||
<Box css={{ width: 100 }}>
|
||||
<Filename name={name} />
|
||||
<Filename variant="label">{name}</Filename>
|
||||
</Box>
|
||||
<Flex align="center" css={{ gap: "$1" }}>
|
||||
<CloudIcon />
|
||||
|
||||
@@ -1,29 +1,101 @@
|
||||
import { Box, Flex, Grid } from "@webstudio-is/design-system";
|
||||
import { useState } from "react";
|
||||
import { findNextListIndex, Grid } from "@webstudio-is/design-system";
|
||||
import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
import { AssetsShell, PreviewAsset, useAssets, useSearch } from "../assets";
|
||||
import { useFilter } from "../assets/use-filter";
|
||||
import { ImageThumbnail } from "./image-thumbnail";
|
||||
import { AssetUpload, useAssets } from "../assets";
|
||||
import { matchSorter } from "match-sorter";
|
||||
|
||||
type ImageManagerProps = {
|
||||
onSelect?: (asset: Asset) => void;
|
||||
const filterItems = (search: string, items: Array<Asset | PreviewAsset>) => {
|
||||
return matchSorter(items, search, {
|
||||
keys: [(item) => item.name],
|
||||
});
|
||||
};
|
||||
|
||||
export const ImageManager = ({ onSelect }: ImageManagerProps) => {
|
||||
const useLogic = ({ onChange }: { onChange?: (asset: Asset) => void }) => {
|
||||
const { assets, handleDelete } = useAssets("image");
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
const { filteredItems, resetFilteredItems, setFilteredItems } = useFilter({
|
||||
items: assets,
|
||||
onReset() {
|
||||
searchProps.onCancel();
|
||||
},
|
||||
});
|
||||
|
||||
const searchProps = useSearch({
|
||||
onCancel: resetFilteredItems,
|
||||
onSearch(search) {
|
||||
if (search === "") {
|
||||
return resetFilteredItems();
|
||||
}
|
||||
const items = filterItems(search, assets);
|
||||
setFilteredItems(items);
|
||||
},
|
||||
onSelect(direction) {
|
||||
if (direction === "current") {
|
||||
setSelectedIndex(selectedIndex);
|
||||
const asset = filteredItems[selectedIndex];
|
||||
if (asset?.status === "uploaded") {
|
||||
onChange?.(asset);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const nextIndex = findNextListIndex(
|
||||
selectedIndex,
|
||||
filteredItems.length,
|
||||
direction
|
||||
);
|
||||
setSelectedIndex(nextIndex);
|
||||
},
|
||||
});
|
||||
|
||||
const handleSelect = (asset?: Asset | PreviewAsset) => {
|
||||
const selectedIndex = filteredItems.findIndex(
|
||||
(item) => item.id === asset?.id
|
||||
);
|
||||
setSelectedIndex(selectedIndex);
|
||||
};
|
||||
|
||||
return {
|
||||
searchProps,
|
||||
handleDelete,
|
||||
filteredItems,
|
||||
handleSelect,
|
||||
selectedIndex,
|
||||
};
|
||||
};
|
||||
|
||||
type ImageManagerProps = {
|
||||
onChange?: (asset: Asset) => void;
|
||||
};
|
||||
|
||||
export const ImageManager = ({ onChange }: ImageManagerProps) => {
|
||||
const {
|
||||
handleDelete,
|
||||
handleSelect,
|
||||
filteredItems,
|
||||
searchProps,
|
||||
selectedIndex,
|
||||
} = useLogic({ onChange });
|
||||
|
||||
return (
|
||||
<Flex gap="3" direction="column" css={{ padding: "$1", paddingTop: "$2" }}>
|
||||
<Box css={{ padding: "$2" }}>
|
||||
<AssetUpload type="image" />
|
||||
</Box>
|
||||
<Grid columns={2} gap={2}>
|
||||
{assets.map((asset) => (
|
||||
<AssetsShell
|
||||
searchProps={searchProps}
|
||||
isEmpty={filteredItems.length === 0}
|
||||
type="image"
|
||||
>
|
||||
<Grid columns={3} gap={2}>
|
||||
{filteredItems.map((asset, index) => (
|
||||
<ImageThumbnail
|
||||
key={asset.id}
|
||||
asset={asset}
|
||||
onDelete={handleDelete}
|
||||
onSelect={onSelect}
|
||||
onSelect={handleSelect}
|
||||
onChange={onChange}
|
||||
state={index === selectedIndex ? "selected" : undefined}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</Flex>
|
||||
</AssetsShell>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Box } from "@webstudio-is/design-system";
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
type KeyboardEvent,
|
||||
type FocusEvent,
|
||||
} from "react";
|
||||
import { Box, styled } from "@webstudio-is/design-system";
|
||||
import placeholderImage from "~/shared/images/image-placeholder.svg";
|
||||
import brokenImage from "~/shared/images/broken-image-placeholder.svg";
|
||||
import { UploadingAnimation } from "./uploading-animation";
|
||||
import { ImageInfoTrigger, imageInfoTriggerCssVars } from "./image-info-tigger";
|
||||
import type { PreviewAsset } from "~/designer/shared/assets";
|
||||
import { Asset } from "@webstudio-is/asset-uploader";
|
||||
import type { Asset } from "@webstudio-is/asset-uploader";
|
||||
import { Filename } from "./filename";
|
||||
|
||||
const useImageWithFallback = ({
|
||||
path = placeholderImage,
|
||||
@@ -24,72 +30,102 @@ const useImageWithFallback = ({
|
||||
return src;
|
||||
};
|
||||
|
||||
type ThumbnailProps = {
|
||||
path?: string;
|
||||
status: Asset["status"];
|
||||
onClick: () => void;
|
||||
};
|
||||
const ThumbnailContainer = styled(Box, {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
flexDirection: "column",
|
||||
border: "2px solid transparent",
|
||||
borderRadius: "$1",
|
||||
outline: 0,
|
||||
gap: "$1",
|
||||
overflow: "hidden",
|
||||
backgroundColor: "$slate4",
|
||||
"&:hover": imageInfoTriggerCssVars({ show: true }),
|
||||
variants: {
|
||||
status: {
|
||||
uploading: {
|
||||
filter: "blur(1px)",
|
||||
opacity: 0.7,
|
||||
},
|
||||
uploaded: {},
|
||||
},
|
||||
state: {
|
||||
selected: {
|
||||
boxShadow:
|
||||
"0px 0px 0px 2px $colors$blue10, 0px 0px 0px 2px $colors$blue10",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const Thumbnail = ({ path, status, onClick }: ThumbnailProps) => {
|
||||
const src = useImageWithFallback({ path });
|
||||
const Thumbnail = styled(Box, {
|
||||
width: "$8",
|
||||
height: "$8",
|
||||
backgroundSize: "contain",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center",
|
||||
flexShrink: 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
css={{
|
||||
backgroundImage: `url("${src}")`,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
backgroundSize: "contain",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundPosition: "center",
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
top: 0,
|
||||
...(status === "uploading"
|
||||
? { filter: "blur(1px)", opacity: 0.7 }
|
||||
: {}),
|
||||
}}
|
||||
onClick={onClick}
|
||||
></Box>
|
||||
);
|
||||
type ImageThumbnailProps = {
|
||||
asset: Asset | PreviewAsset;
|
||||
onDelete: (ids: Array<string>) => void;
|
||||
onSelect: (asset?: Asset | PreviewAsset) => void;
|
||||
onChange?: (asset: Asset) => void;
|
||||
state?: "selected";
|
||||
};
|
||||
|
||||
export const ImageThumbnail = ({
|
||||
asset,
|
||||
onDelete,
|
||||
onSelect,
|
||||
}: {
|
||||
asset: Asset | PreviewAsset;
|
||||
onDelete: (ids: Array<string>) => void;
|
||||
onSelect?: (asset: Asset) => void;
|
||||
}) => {
|
||||
onChange,
|
||||
state,
|
||||
}: ImageThumbnailProps) => {
|
||||
const { path, status, name } = asset;
|
||||
const description =
|
||||
"description" in asset && asset.description ? asset.description : name;
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const isUploading = status === "uploading";
|
||||
const isUploadedAsset = isUploading === false && "size" in asset;
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const src = useImageWithFallback({ path });
|
||||
|
||||
return (
|
||||
<Box
|
||||
<ThumbnailContainer
|
||||
title={description}
|
||||
css={{
|
||||
aspectRatio: "1/1",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
padding: "0 $2",
|
||||
position: "relative",
|
||||
"&:hover": imageInfoTriggerCssVars({ show: true }),
|
||||
tabIndex={0}
|
||||
status={status}
|
||||
state={state}
|
||||
onFocus={() => onSelect?.(asset)}
|
||||
onBlur={(event: FocusEvent) => {
|
||||
const isFocusWithin = event.currentTarget.contains(event.relatedTarget);
|
||||
if (isFocusWithin === false) {
|
||||
onSelect();
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event: KeyboardEvent) => {
|
||||
if (event.code === "Enter" && asset.status === "uploaded") {
|
||||
onChange?.(asset);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Thumbnail
|
||||
path={path}
|
||||
status={status}
|
||||
css={{ backgroundImage: `url("${src}")` }}
|
||||
onClick={() => {
|
||||
if (isUploadedAsset) onSelect?.(asset);
|
||||
if (isUploadedAsset) onChange?.(asset);
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
css={{
|
||||
width: "100%",
|
||||
// @todo should be a token from design system
|
||||
height: 12,
|
||||
}}
|
||||
>
|
||||
<Filename variant={"tiny"}>{name}</Filename>
|
||||
</Box>
|
||||
{isUploadedAsset && (
|
||||
<ImageInfoTrigger
|
||||
asset={asset}
|
||||
@@ -100,6 +136,6 @@ export const ImageThumbnail = ({
|
||||
/>
|
||||
)}
|
||||
{(isUploading || isDeleting) && <UploadingAnimation />}
|
||||
</Box>
|
||||
</ThumbnailContainer>
|
||||
);
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import type { ImageMeta } from "./utils/format-asset";
|
||||
|
||||
type BaseAsset = Omit<DbAsset, "meta"> & {
|
||||
path: string;
|
||||
status?: "uploading" | "uploaded";
|
||||
status?: "uploaded";
|
||||
};
|
||||
|
||||
export type FontAsset = Omit<BaseAsset, "format"> & {
|
||||
|
||||
Reference in new issue
Block a user