[ENG-1506] Tags in Explorer (#2532)

* wip

* tags in explorer

* cleanup

* i18n
This commit is contained in:
ameer2468 2024-06-01 22:11:31 +03:00 committed by GitHub
parent 48f5715839
commit a6cb61584e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 106 additions and 23 deletions

View file

@ -174,6 +174,15 @@ export default () => {
explorerLayout.showPathBar = value;
}}
/>
<RadixCheckbox
checked={layoutStore.showTags}
label={t('show_tags')}
name="showTags"
onCheckedChange={(value) => {
if (typeof value !== 'boolean') return;
explorerLayout.showTags = value;
}}
/>
{settings.layoutMode === 'grid' && (
<RadixCheckbox

View file

@ -1,12 +1,15 @@
import clsx from 'clsx';
import { memo, useMemo } from 'react';
import {
Tag,
getItemFilePath,
getItemObject,
humanizeSize,
useExplorerLayoutStore,
useLibraryQuery,
useSelector,
type ExplorerItem
} from '@sd/client';
import clsx from 'clsx';
import { memo, useMemo } from 'react';
import { useLocale } from '~/hooks';
import { useExplorerContext } from '../../../Context';
@ -52,7 +55,6 @@ export const GridViewItem = memo((props: GridViewItemProps) => {
const InnerDroppable = () => {
const item = useGridViewItemContext();
const { isDroppable } = useExplorerDroppableContext();
return (
<>
<div
@ -63,7 +65,6 @@ const InnerDroppable = () => {
>
<ItemFileThumb />
</div>
<ItemMetadata />
</>
);
@ -103,6 +104,7 @@ const ItemFileThumb = () => {
const ItemMetadata = () => {
const item = useGridViewItemContext();
const { isDroppable } = useExplorerDroppableContext();
const explorerLayout = useExplorerLayoutStore();
const isRenaming = useSelector(explorerStore, (s) => s.isRenaming && item.selected);
@ -117,11 +119,35 @@ const ItemMetadata = () => {
selected={item.selected}
/>
<ItemSize />
{explorerLayout.showTags && <ItemTags />}
{item.data.type === 'Label' && <LabelItemCount data={item.data} />}
</ExplorerDraggable>
);
};
const ItemTags = () => {
const item = useGridViewItemContext();
const object = getItemObject(item.data);
const filePath = getItemFilePath(item.data);
const data = object || filePath;
const tags = data && 'tags' in data ? data.tags : [];
return (
<div
className='relative mt-1 flex w-full flex-row items-center justify-center'
style={{
left: tags.length * 1
}}
>
{tags?.slice(0, 3).map((tag: {tag: Tag}, i: number) => (
<div key={tag.tag.id} className='relative size-2.5 rounded-full border border-app' style={{
backgroundColor: tag.tag.color!,
right: i * 4,
}}/>
))}
</div>
);
}
const ItemSize = () => {
const item = useGridViewItemContext();
const { showBytesInGridView } = useExplorerContext().useSettingsSnapshot();

View file

@ -1,6 +1,7 @@
import { Grid, useGrid } from '@virtual-grid/react';
import { useCallback } from 'react';
import { useExplorerLayoutStore } from '@sd/client';
import { useExplorerContext } from '../../Context';
import { getItemData, getItemId, uniqueId } from '../../util';
import { useExplorerViewContext } from '../Context';
@ -15,10 +16,12 @@ export const GridView = () => {
const explorer = useExplorerContext();
const explorerView = useExplorerViewContext();
const explorerSettings = explorer.useSettingsSnapshot();
const layoutStore = useExplorerLayoutStore();
const itemDetailsHeight = 44 + (explorerSettings.showBytesInGridView ? 20 : 0);
const itemDetailsHeight = (layoutStore.showTags ? 60 : 44) + (explorerSettings.showBytesInGridView ? 20 : 0);
const itemHeight = explorerSettings.gridItemSize + itemDetailsHeight;
const BOTTOM_PADDING = layoutStore.showTags ? 16 : 12;
const grid = useGrid({
scrollRef: explorer.scrollRef,
count: explorer.items?.length ?? 0,
@ -26,7 +29,7 @@ export const GridView = () => {
columns: 'auto',
size: { width: explorerSettings.gridItemSize, height: itemHeight },
padding: {
bottom: PADDING + (explorerView.scrollPadding?.bottom ?? 0),
bottom: BOTTOM_PADDING + (explorerView.scrollPadding?.bottom ?? 0),
x: PADDING,
y: PADDING
},

View file

@ -1,7 +1,7 @@
import { getItemFilePath, useSelector, type ExplorerItem } from '@sd/client';
import { flexRender, type Cell } from '@tanstack/react-table';
import clsx from 'clsx';
import { memo, useMemo } from 'react';
import { getItemFilePath, useSelector, type ExplorerItem } from '@sd/client';
import { TABLE_PADDING_X } from '.';
import { useExplorerContext } from '../../Context';

View file

@ -1,3 +1,13 @@
import {
getExplorerItemData,
getIndexedItemFilePath,
getItemFilePath,
getItemObject,
humanizeSize,
useExplorerLayoutStore,
useSelector,
type ExplorerItem
} from '@sd/client';
import {
CellContext,
functionalUpdate,
@ -9,15 +19,6 @@ import clsx from 'clsx';
import dayjs from 'dayjs';
import { memo, useMemo } from 'react';
import { stringify } from 'uuid';
import {
getExplorerItemData,
getIndexedItemFilePath,
getItemFilePath,
getItemObject,
humanizeSize,
useSelector,
type ExplorerItem
} from '@sd/client';
import { useLocale } from '~/hooks';
import { useExplorerContext } from '../../Context';
@ -48,6 +49,7 @@ const NameCell = memo(({ item, selected }: { item: ExplorerItem; selected: boole
const explorer = useExplorerContext();
const explorerSettings = explorer.useSettingsSnapshot();
const explorerLayout = useExplorerLayoutStore();
return (
<div className="flex">
@ -66,15 +68,41 @@ const NameCell = memo(({ item, selected }: { item: ExplorerItem; selected: boole
selected={selected}
allowHighlight={false}
style={{ fontSize: LIST_VIEW_TEXT_SIZES[explorerSettings.listViewTextSize] }}
className="absolute top-1/2 z-10 max-w-full -translate-y-1/2"
idleClassName="!w-full"
className="absolute top-1/2 z-10 -translate-y-1/2"
idleClassName={clsx(explorerLayout.showTags ? '!w-4/5' : '!w-full')}
editLines={3}
/>
{explorerLayout.showTags && (
<Tags item={item}/>
)}
</div>
</div>
);
});
const Tags = ({item}: {item: ExplorerItem}) => {
const object = getItemObject(item);
const filePath = getItemFilePath(item);
const data = object || filePath;
const tags = data && 'tags' in data ? data.tags : [];
return (
<div
className='relative flex size-full flex-row items-center justify-end self-center'
style={{
marginLeft: tags.length * 4
}}
>
{tags.map(({tag}, i: number) => (
<div key={tag.id} className='relative size-2.5 rounded-full border border-app' style={{
backgroundColor: tag.color || 'transparent',
right: i * 4
}}/>
))}
</div>
)
}
const KindCell = ({ kind }: { kind: string }) => {
const explorer = useExplorerContext();
const explorerSettings = explorer.useSettingsSnapshot();

View file

@ -1,7 +1,3 @@
import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import { proxy, snapshot, subscribe, useSnapshot } from 'valtio';
import { z } from 'zod';
import type {
ExplorerItem,
ExplorerLayout,
@ -12,6 +8,10 @@ import type {
Tag
} from '@sd/client';
import { ObjectKindEnum, type Ordering, type OrderingKeys } from '@sd/client';
import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import { proxy, snapshot, subscribe, useSnapshot } from 'valtio';
import { z } from 'zod';
import { createDefaultExplorerSettings } from './store';
import { uniqueId } from './util';

View file

@ -5,9 +5,10 @@ import {
ToastDefautlColor,
useLibraryMutation,
usePlausibleEvent,
useRspcLibraryContext,
useZodForm
} from '@sd/client';
import { Dialog, InputField, useDialog, UseDialogProps, z } from '@sd/ui';
import { Dialog, InputField, UseDialogProps, useDialog, z } from '@sd/ui';
import { ColorPicker } from '~/components';
import { useLocale } from '~/hooks';
@ -22,10 +23,12 @@ export type AssignTagItems = Array<
export function useAssignItemsToTag() {
const submitPlausibleEvent = usePlausibleEvent();
const rspc = useRspcLibraryContext();
const mutation = useLibraryMutation(['tags.assign'], {
onSuccess: () => {
submitPlausibleEvent({ event: { type: 'tagAssign' } });
rspc.queryClient.invalidateQueries(['search.paths']);
}
});

View file

@ -614,6 +614,7 @@
"show_object_size": "إظهار حجم الكائن",
"show_path_bar": "إظهار شريط المسار",
"show_slider": "إظهار المنزلق",
"show_tags": "أضهر العلامات",
"size": "الحجم",
"size_b": "بايت",
"size_bs": "ب",

View file

@ -605,6 +605,7 @@
"show_object_size": "Памер аб'екта",
"show_path_bar": "Адрасны радок",
"show_slider": "Паказаць паўзунок",
"show_tags": "Паказаць тэгі",
"size": "Памер",
"size_b": "Б",
"size_bs": "Б",

View file

@ -602,6 +602,7 @@
"show_object_size": "Objektgröße anzeigen",
"show_path_bar": "Pfadleiste anzeigen",
"show_slider": "Slider anzeigen",
"show_tags": "Tags anzeigen",
"size": "Größe",
"size_b": "B",
"size_bs": "B",

View file

@ -614,6 +614,7 @@
"show_object_size": "Show Object size",
"show_path_bar": "Show Path Bar",
"show_slider": "Show slider",
"show_tags": "Show Tags",
"size": "Size",
"size_b": "B",
"size_bs": "Bs",

View file

@ -604,6 +604,7 @@
"show_object_size": "Mostrar Tamaño del Objeto",
"show_path_bar": "Mostrar Barra de Ruta",
"show_slider": "Mostrar deslizador",
"show_tags": "Mostrar etiquetas",
"size": "Tamaño",
"size_b": "B",
"size_bs": "B",

View file

@ -604,6 +604,7 @@
"show_object_size": "Afficher la taille de l'objet",
"show_path_bar": "Afficher la barre de chemin",
"show_slider": "Afficher le curseur",
"show_tags": "Voir les étiquettes",
"size": "Taille",
"size_b": "o",
"size_bs": "B",

View file

@ -604,6 +604,7 @@
"show_object_size": "Mostra la dimensione dell'oggetto",
"show_path_bar": "Mostra barra del percorso",
"show_slider": "Mostra slider",
"show_tags": "Mostra tag",
"size": "Dimensione",
"size_b": "B",
"size_bs": "B",

View file

@ -598,6 +598,7 @@
"show_object_size": "ファイルサイズを表示",
"show_path_bar": "パスバーを表示",
"show_slider": "スライダーを表示",
"show_tags": "タグを表示",
"size": "サイズ",
"size_b": "バイト",
"size_bs": "B",

View file

@ -602,6 +602,7 @@
"show_object_size": "Toon Object grootte",
"show_path_bar": "Padbalk Tonen",
"show_slider": "Toon schuifregelaar",
"show_tags": "Toon labels",
"size": "Grootte",
"size_b": "B",
"size_bs": "B",

View file

@ -605,6 +605,7 @@
"show_object_size": "Размер объекта",
"show_path_bar": "Адресная строка",
"show_slider": "Показать ползунок",
"show_tags": "Показать теги",
"size": "Размер",
"size_b": "Б",
"size_bs": "Б",

View file

@ -602,6 +602,7 @@
"show_object_size": "Nesne Boyutunu Göster",
"show_path_bar": "Yol Çubuğunu Göster",
"show_slider": "Kaydırıcıyı Göster",
"show_tags": "Etiketleri göster",
"size": "Boyut",
"size_b": "B",
"size_bs": "B",

View file

@ -598,6 +598,7 @@
"show_object_size": "显示对象大小",
"show_path_bar": "显示路径栏",
"show_slider": "显示滑块",
"show_tags": "显示标签",
"size": "大小",
"size_b": "乙",
"size_bs": "乙",

View file

@ -598,6 +598,7 @@
"show_object_size": "顯示對象大小",
"show_path_bar": "顯示路徑條",
"show_slider": "顯示滑塊",
"show_tags": "顯示標籤",
"size": "大小",
"size_b": "乙",
"size_bs": "乙",

View file

@ -7,6 +7,7 @@ export const explorerLayout = createPersistedMutable(
'sd-explorer-layout',
createMutable({
showPathBar: true,
showTags: true,
showImageSlider: true,
// might move this to a store called settings
defaultView: 'grid' as ExplorerLayout