Skip to content

Commit 89895a7

Browse files
authored
chore(studio): file explorer rows (#43845)
## What kind of change does this PR introduce? UI touch-up ## What is the current behavior? Explorer rows in file storage is a bit rough around the edges. ## What is the new behavior? - Consistent loaders - Consistent icons Also includes: - Fix to `stroke-width` on files-bucket.svg as documented in icons.mdx | Before | After | | --- | --- | | <img width="1024" height="563" alt="Buckets Storage AWS Healthy Toolshed Supabase" src="https://github.com/user-attachments/assets/055518b2-6d93-4c9d-879b-6e7dd51e0620" /> | <img width="1024" height="563" alt="Buckets Storage AWS Healthy Toolshed Supabase" src="https://github.com/user-attachments/assets/fae05446-95d9-4ca1-b441-7cd39d1c6036" /> |
1 parent d49da89 commit 89895a7

9 files changed

Lines changed: 56 additions & 89 deletions

File tree

apps/design-system/content/docs/icons.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function app() {
4545
}
4646
```
4747

48-
**Default props**: All icons default to `size={24}`. Stroke and fill defaults come from the source SVG's root attributes (e.g. `stroke-width="1"` in the source becomes the component's default `strokeWidth`). Override these props as needed for your use case.
48+
**Default props**: All icons default to `size={24}`. Stroke and fill defaults come from the source SVG's root attributes (e.g. `stroke-width="1"` on the root `<svg>` becomes the component's default `strokeWidth`). You can override those defaults at the call site, but any `stroke`, `fill`, or `stroke-width` set on individual child paths will still win.
4949

5050
### Adding new custom icons
5151

@@ -63,7 +63,7 @@ Follow these steps to add a new custom icon to the Supabase icon library.
6363

6464
For **fill-only icons** (e.g. logos that use shapes instead of strokes), add `stroke="none"` to the root `<svg>` element. The build will propagate this so the component never renders an unwanted stroke.
6565

66-
Leave attributes like `stroke-width` as they are. The root SVG's `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` attributes are automatically propagated as the component's defaults by the build process. The conversion to camel-case for React compatibility (e.g. `strokeWidth`) is also handled automatically.
66+
Prefer putting shared styling like `fill`, `stroke`, `stroke-width`, `stroke-linecap`, and `stroke-linejoin` on the root `<svg>`. The build propagates those root attributes as the component's defaults, and also converts them to camel-case for React compatibility (e.g. `strokeWidth`). If you put those attributes on individual child paths instead, they become fixed path-level styling and will override props passed to the component.
6767

6868
2. **Build the component**: Run `npm run build:icons` from inside the `packages/icons` directory
6969

@@ -133,4 +133,4 @@ Note `stroke="none"` on the root to prevent unwanted strokes, and `fill="current
133133

134134
### Troubleshooting
135135

136-
If your SVG specifies `stroke-width` attributes, they will override the component's `strokeWidth` prop. Remove stroke attributes from individual paths to let the component control them.
136+
If your SVG specifies `stroke-width` attributes on individual child paths, they will override the component's `strokeWidth` prop. Keep shared stroke attributes on the root `<svg>` and remove them from individual paths if you want consumers to control stroke weight.

apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerHeader.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
Edit2,
1313
FolderPlus,
1414
List,
15-
Loader,
15+
LoaderCircle,
1616
RefreshCw,
1717
Search,
1818
Upload,
@@ -61,7 +61,7 @@ const HeaderPathEdit = ({ loading, isSearching, breadcrumbs, togglePathEdit }: a
6161
>
6262
{loading.isLoading ? (
6363
<div className="ml-2 flex items-center gap-x-3">
64-
<Loader size={14} strokeWidth={2} className="animate-spin" />
64+
<LoaderCircle size={14} strokeWidth={2} className="animate-spin" />
6565
<p className="text-sm text-foreground-light">{loading.message}</p>
6666
</div>
6767
) : (
@@ -113,7 +113,7 @@ const HeaderBreadcrumbs = ({
113113

114114
return loading.isLoading ? (
115115
<div className="ml-2 flex items-center">
116-
<Loader size={16} strokeWidth={2} className="animate-spin" />
116+
<LoaderCircle size={14} strokeWidth={2} className="animate-spin text-foreground-lighter" />
117117
<p className="ml-3 text-sm">{loading.message}</p>
118118
</div>
119119
) : (

apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { PermissionAction } from '@supabase/shared-types/out/constants'
2+
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
3+
import { FilesBucket as FilesBucketIcon } from 'icons'
4+
import { formatBytes } from 'lib/helpers'
25
import { find, isEmpty, isEqual } from 'lodash'
36
import {
47
AlertCircle,
@@ -7,20 +10,16 @@ import {
710
Edit,
811
File,
912
Film,
13+
FolderOpen,
1014
Image,
11-
Loader,
15+
LoaderCircle,
1216
MoreVertical,
1317
Move,
1418
Music,
1519
Trash2,
1620
} from 'lucide-react'
17-
import { useContextMenu } from 'react-contexify'
18-
import SVG from 'react-inlinesvg'
19-
20-
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
21-
import { BASE_PATH } from 'lib/constants'
22-
import { formatBytes } from 'lib/helpers'
2321
import type { CSSProperties } from 'react'
22+
import { useContextMenu } from 'react-contexify'
2423
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
2524
import {
2625
Checkbox,
@@ -38,6 +37,7 @@ import {
3837
TooltipContent,
3938
TooltipTrigger,
4039
} from 'ui'
40+
4141
import {
4242
CONTEXT_MENU_KEYS,
4343
STORAGE_ROW_STATUS,
@@ -54,47 +54,42 @@ export const RowIcon = ({
5454
view,
5555
status,
5656
fileType,
57+
isOpened = false,
5758
mimeType,
5859
}: {
5960
view: STORAGE_VIEWS
6061
status: STORAGE_ROW_STATUS
6162
fileType: string
63+
isOpened?: boolean
6264
mimeType: string | undefined
6365
}) => {
6466
if (view === STORAGE_VIEWS.LIST && status === STORAGE_ROW_STATUS.LOADING) {
65-
return <Loader size={16} strokeWidth={2} className="animate-spin" />
67+
return (
68+
<LoaderCircle size={14} strokeWidth={2} className="animate-spin text-foreground-lighter" />
69+
)
6670
}
6771

68-
if (fileType === STORAGE_ROW_TYPES.BUCKET || fileType === STORAGE_ROW_TYPES.FOLDER) {
69-
const iconSrc =
70-
fileType === STORAGE_ROW_TYPES.BUCKET
71-
? `${BASE_PATH}/img/bucket-filled.svg`
72-
: fileType === STORAGE_ROW_TYPES.FOLDER
73-
? `${BASE_PATH}/img/folder-filled.svg`
74-
: `${BASE_PATH}/img/file-filled.svg`
75-
return (
76-
<SVG
77-
src={iconSrc}
78-
preProcessor={(code) =>
79-
code.replace(/svg/, 'svg class="w-4 h-4 text-color-inherit opacity-75"')
80-
}
81-
/>
72+
if (fileType === STORAGE_ROW_TYPES.FOLDER) {
73+
return isOpened ? (
74+
<FolderOpen size={16} strokeWidth={2} className="text-foreground-lighter" />
75+
) : (
76+
<FilesBucketIcon size={16} strokeWidth={2} className="text-foreground-lighter" />
8277
)
8378
}
8479

8580
if (mimeType?.includes('image')) {
86-
return <Image size={16} strokeWidth={2} />
81+
return <Image size={16} className="text-foreground-lighter" />
8782
}
8883

8984
if (mimeType?.includes('audio')) {
90-
return <Music size={16} strokeWidth={2} />
85+
return <Music size={16} strokeWidth={2} className="text-foreground-lighter" />
9186
}
9287

9388
if (mimeType?.includes('video')) {
94-
return <Film size={16} strokeWidth={2} />
89+
return <Film size={16} strokeWidth={2} className="text-foreground-lighter" />
9590
}
9691

97-
return <File size={16} strokeWidth={2} />
92+
return <File size={16} strokeWidth={2} className="text-foreground-lighter" />
9893
}
9994

10095
interface FileExplorerRowProps {
@@ -173,27 +168,27 @@ export const FileExplorerRow = ({
173168
? [
174169
{
175170
name: 'Rename',
176-
icon: <Edit size={14} strokeWidth={1} />,
171+
icon: <Edit size={12} className="text-foreground-light" />,
177172
onClick: () => setSelectedItemToRename(itemWithColumnIndex),
178173
},
179174
]
180175
: []),
181176
{
182177
name: 'Download',
183-
icon: <Download size={14} strokeWidth={1} />,
178+
icon: <Download size={12} className="text-foreground-light" />,
184179
onClick: () => downloadFolder(itemWithColumnIndex),
185180
},
186181
{
187182
name: 'Copy path to folder',
188-
icon: <Copy size={14} strokeWidth={1} />,
183+
icon: <Copy size={12} className="text-foreground-light" />,
189184
onClick: () => copyPathToFolder(openedFolders, itemWithColumnIndex),
190185
},
191186
...(canUpdateFiles
192187
? [
193188
{ name: 'Separator', icon: undefined, onClick: undefined },
194189
{
195190
name: 'Delete',
196-
icon: <Trash2 size={14} strokeWidth={1} />,
191+
icon: <Trash2 size={12} className="text-foreground-light" />,
197192
onClick: () => setSelectedItemsToDelete([itemWithColumnIndex]),
198193
},
199194
]
@@ -206,14 +201,14 @@ export const FileExplorerRow = ({
206201
? [
207202
{
208203
name: 'Get URL',
209-
icon: <Copy size={14} strokeWidth={1} />,
204+
icon: <Copy size={12} className="text-foreground-light" />,
210205
onClick: () => onCopyUrl(itemWithColumnIndex.name),
211206
},
212207
]
213208
: [
214209
{
215210
name: 'Get URL',
216-
icon: <Copy size={14} strokeWidth={1} />,
211+
icon: <Copy size={12} className="text-foreground-light" />,
217212
children: [
218213
{
219214
name: 'Expire in 1 week',
@@ -239,19 +234,19 @@ export const FileExplorerRow = ({
239234
]),
240235
{
241236
name: 'Download',
242-
icon: <Download size={14} strokeWidth={1} />,
237+
icon: <Download size={12} className="text-foreground-light" />,
243238
onClick: () => downloadFile(itemWithColumnIndex),
244239
},
245240
...(canUpdateFiles
246241
? [
247242
{
248243
name: 'Rename',
249-
icon: <Edit size={14} strokeWidth={1} />,
244+
icon: <Edit size={12} className="text-foreground-light" />,
250245
onClick: () => setSelectedItemToRename(itemWithColumnIndex),
251246
},
252247
{
253248
name: 'Move',
254-
icon: <Move size={14} strokeWidth={1} />,
249+
icon: <Move size={12} className="text-foreground-light" />,
255250
onClick: () => setSelectedItemsToMove([itemWithColumnIndex]),
256251
},
257252
{ name: 'Separator', icon: undefined, onClick: undefined },
@@ -263,7 +258,7 @@ export const FileExplorerRow = ({
263258
? [
264259
{
265260
name: 'Delete',
266-
icon: <Trash2 size={14} strokeWidth={1} />,
261+
icon: <Trash2 size={12} className="text-foreground-light" />,
267262
onClick: () => setSelectedItemsToDelete([itemWithColumnIndex]),
268263
},
269264
]
@@ -324,7 +319,7 @@ export const FileExplorerRow = ({
324319
event.stopPropagation()
325320
event.preventDefault()
326321
if (item.status !== STORAGE_ROW_STATUS.LOADING && !isOpened && !isPreviewed) {
327-
item.type === STORAGE_ROW_TYPES.FOLDER || item.type === STORAGE_ROW_TYPES.BUCKET
322+
item.type === STORAGE_ROW_TYPES.FOLDER
328323
? openFolder(columnIndex, item)
329324
: onSelectFile(columnIndex)
330325
}
@@ -348,6 +343,7 @@ export const FileExplorerRow = ({
348343
view={view}
349344
status={item.status}
350345
fileType={item.type}
346+
isOpened={isOpened}
351347
mimeType={item.metadata?.mimetype}
352348
/>
353349
</div>
@@ -398,9 +394,9 @@ export const FileExplorerRow = ({
398394
}
399395
>
400396
{item.status === STORAGE_ROW_STATUS.LOADING ? (
401-
<Loader
402-
className={`animate-spin ${view === STORAGE_VIEWS.LIST ? 'invisible' : ''}`}
403-
size={16}
397+
<LoaderCircle
398+
className={`animate-spin text-foreground-lighter ${view === STORAGE_VIEWS.LIST ? 'invisible' : ''}`}
399+
size={14}
404400
strokeWidth={2}
405401
/>
406402
) : (

apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { Transition } from '@headlessui/react'
22
import { PermissionAction } from '@supabase/shared-types/out/constants'
3-
import { isEmpty } from 'lodash'
4-
import { AlertCircle, ChevronDown, Copy, Download, Loader, Trash2, X } from 'lucide-react'
5-
import SVG from 'react-inlinesvg'
6-
73
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
84
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
95
import { BASE_PATH } from 'lib/constants'
106
import { formatBytes } from 'lib/helpers'
7+
import { isEmpty } from 'lodash'
8+
import { AlertCircle, ChevronDown, Copy, Download, LoaderCircle, Trash2, X } from 'lucide-react'
9+
import SVG from 'react-inlinesvg'
1110
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
1211
import {
1312
Button,
@@ -16,6 +15,7 @@ import {
1615
DropdownMenuItem,
1716
DropdownMenuTrigger,
1817
} from 'ui'
18+
1919
import { URL_EXPIRY_DURATION } from '../Storage.constants'
2020
import { StorageItem } from '../Storage.types'
2121
import { useCopyUrl } from './useCopyUrl'
@@ -41,7 +41,7 @@ const PreviewFile = ({ item }: { item: StorageItem }) => {
4141
if (isLoading) {
4242
return (
4343
<div className="flex h-full w-full items-center justify-center text-foreground-lighter">
44-
<Loader size={14} strokeWidth={2} className="animate-spin" />
44+
<LoaderCircle size={14} strokeWidth={2} className="animate-spin text-foreground-lighter" />
4545
</div>
4646
)
4747
}

apps/studio/public/img/bucket-filled.svg

Lines changed: 0 additions & 15 deletions
This file was deleted.

apps/studio/public/img/folder-filled.svg

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)