fix(filter): improve filter UX - empty states + show all indicators (#1039)

* fix(filter): improve filter UX with search icon, empty states, and show-all indicators

* fix

* fix

---------

Co-authored-by: Guy Ben-Aharon <baguy3@gmail.com>
This commit is contained in:
Jonathan Fishner
2026-01-01 18:13:21 +02:00
committed by GitHub
parent a43cc30c52
commit 8db2ddff55
27 changed files with 575 additions and 44 deletions

View File

@@ -36,7 +36,11 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
areas,
diagramId,
} = useChartDB();
const { filter, loading: filterLoading } = useDiagramFilter();
const {
filter,
loading: filterLoading,
hasActiveFilter,
} = useDiagramFilter();
const { showDBViews } = useLocalConfig();
const { fitView, screenToFlowPosition, setNodes } = useReactFlow();
const [overlapGraph, setOverlapGraph] =
@@ -73,8 +77,11 @@ export const CanvasProvider = ({ children }: CanvasProviderProps) => {
diagramIdActiveFilterRef.current = diagramId;
setShowFilter(true);
}, [filterLoading, diagramId]);
// Only show filter if there's an active filter
if (hasActiveFilter) {
setShowFilter(true);
}
}, [filterLoading, diagramId, hasActiveFilter]);
const reorderTables = useCallback(
(

View File

@@ -128,6 +128,8 @@ export const ar: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'جميع الجداول مخفية',
show_all: 'عرض الكل',
table: {
fields: 'الحقول',
@@ -517,6 +519,22 @@ export const ar: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'جميع الجداول مخفية',
show_all_tables: 'عرض الكل',
},
canvas_filter: {
title: 'تصفية الجداول',
search_placeholder: 'البحث في الجداول...',
group_by_schema: 'تجميع حسب المخطط',
group_by_area: 'تجميع حسب المنطقة',
no_tables_found: 'لم يتم العثور على جداول',
empty_diagram_description: 'أنشئ جدولاً للبدء',
no_tables_description: 'جرب تعديل البحث أو التصفية',
clear_filter: 'مسح التصفية',
},
snap_to_grid_tooltip: '({{key}} مغنظة الشبكة (اضغط مع الاستمرار على',
tool_tips: {

View File

@@ -129,6 +129,8 @@ export const bn: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'সব টেবিল লুকানো আছে',
show_all: 'সব দেখান',
table: {
fields: 'ফিল্ড',
@@ -524,6 +526,22 @@ export const bn: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'সব টেবিল লুকানো আছে',
show_all_tables: 'সব দেখান',
},
canvas_filter: {
title: 'টেবিল ফিল্টার করুন',
search_placeholder: 'টেবিল খুঁজুন...',
group_by_schema: 'স্কিমা অনুযায়ী গ্রুপ করুন',
group_by_area: 'এলাকা অনুযায়ী গ্রুপ করুন',
no_tables_found: 'কোনো টেবিল পাওয়া যায়নি',
empty_diagram_description: 'শুরু করতে একটি টেবিল তৈরি করুন',
no_tables_description: 'আপনার অনুসন্ধান বা ফিল্টার সামঞ্জস্য করুন',
clear_filter: 'ফিল্টার মুছুন',
},
snap_to_grid_tooltip: 'গ্রিডে স্ন্যাপ করুন (অবস্থান {{key}})',
tool_tips: {

View File

@@ -130,6 +130,8 @@ export const de: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Alle Tabellen sind ausgeblendet',
show_all: 'Alle anzeigen',
table: {
fields: 'Felder',
@@ -527,6 +529,24 @@ export const de: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Alle Tabellen sind ausgeblendet',
show_all_tables: 'Alle anzeigen',
},
canvas_filter: {
title: 'Tabellen filtern',
search_placeholder: 'Tabellen suchen...',
group_by_schema: 'Nach Schema gruppieren',
group_by_area: 'Nach Bereich gruppieren',
no_tables_found: 'Keine Tabellen gefunden',
empty_diagram_description:
'Erstellen Sie eine Tabelle, um zu beginnen',
no_tables_description:
'Versuchen Sie, Ihre Suche oder Filter anzupassen',
clear_filter: 'Filter löschen',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -125,6 +125,8 @@ export const en = {
no_results: 'No tables found matching your filter.',
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'All tables are hidden',
show_all: 'Show all',
table: {
fields: 'Fields',
@@ -512,6 +514,22 @@ export const en = {
add_relationship: 'Add Relationship',
},
canvas: {
all_tables_hidden: 'All tables are hidden',
show_all_tables: 'Show all',
},
canvas_filter: {
title: 'Filter Tables',
search_placeholder: 'Search tables...',
group_by_schema: 'Group by Schema',
group_by_area: 'Group by Area',
no_tables_found: 'No tables found',
empty_diagram_description: 'Create a table to get started',
no_tables_description: 'Try adjusting your search or filter',
clear_filter: 'Clear filter',
},
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',
tool_tips: {

View File

@@ -128,6 +128,8 @@ export const es: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Todas las tablas están ocultas',
show_all: 'Mostrar todo',
table: {
fields: 'Campos',
@@ -526,6 +528,22 @@ export const es: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Todas las tablas están ocultas',
show_all_tables: 'Mostrar todo',
},
canvas_filter: {
title: 'Filtrar Tablas',
search_placeholder: 'Buscar tablas...',
group_by_schema: 'Agrupar por Esquema',
group_by_area: 'Agrupar por Área',
no_tables_found: 'No se encontraron tablas',
empty_diagram_description: 'Crea una tabla para comenzar',
no_tables_description: 'Intenta ajustar tu búsqueda o filtro',
clear_filter: 'Limpiar filtro',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -126,6 +126,8 @@ export const fr: LanguageTranslation = {
'Aucune table trouvée correspondant à votre filtre.',
show_list: 'Afficher la Liste des Tableaux',
show_dbml: "Afficher l'éditeur DBML",
all_hidden: 'Toutes les tables sont masquées',
show_all: 'Tout afficher',
table: {
fields: 'Champs',
@@ -522,6 +524,23 @@ export const fr: LanguageTranslation = {
add_relationship: 'Ajouter une Relation',
},
canvas: {
all_tables_hidden: 'Toutes les tables sont masquées',
show_all_tables: 'Tout afficher',
},
canvas_filter: {
title: 'Filtrer les Tables',
search_placeholder: 'Rechercher des tables...',
group_by_schema: 'Grouper par Schéma',
group_by_area: 'Grouper par Zone',
no_tables_found: 'Aucune table trouvée',
empty_diagram_description: 'Créez une table pour commencer',
no_tables_description:
'Essayez de modifier votre recherche ou filtre',
clear_filter: 'Effacer le filtre',
},
snap_to_grid_tooltip:
'Aligner sur la grille (maintenir la touche {{key}})',

View File

@@ -129,6 +129,8 @@ export const gu: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'બધી ટેબલ્સ છુપાયેલી છે',
show_all: 'બધું બતાવો',
table: {
fields: 'ફીલ્ડ્સ',
@@ -525,6 +527,23 @@ export const gu: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'બધી ટેબલ્સ છુપાયેલી છે',
show_all_tables: 'બધું બતાવો',
},
canvas_filter: {
title: 'ટેબલ્સ ફિલ્ટર કરો',
search_placeholder: 'ટેબલ્સ શોધો...',
group_by_schema: 'સ્કીમા પ્રમાણે ગ્રુપ કરો',
group_by_area: 'વિસ્તાર પ્રમાણે ગ્રુપ કરો',
no_tables_found: 'કોઈ ટેબલ મળી નથી',
empty_diagram_description: 'શરૂ કરવા માટે ટેબલ બનાવો',
no_tables_description:
'તમારી શોધ અથવા ફિલ્ટર સમાયોજિત કરવાનો પ્રયાસ કરો',
clear_filter: 'ફિલ્ટર સાફ કરો',
},
snap_to_grid_tooltip: 'ગ્રિડ પર સ્નેપ કરો (જમાવટ {{key}})',
tool_tips: {

View File

@@ -129,6 +129,8 @@ export const hi: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'सभी तालिकाएँ छिपी हुई हैं',
show_all: 'सभी दिखाएं',
table: {
fields: 'फ़ील्ड्स',
@@ -527,6 +529,23 @@ export const hi: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'सभी तालिकाएँ छिपी हुई हैं',
show_all_tables: 'सभी दिखाएं',
},
canvas_filter: {
title: 'तालिकाएँ फ़िल्टर करें',
search_placeholder: 'तालिकाएँ खोजें...',
group_by_schema: 'स्कीमा के अनुसार समूहित करें',
group_by_area: 'क्षेत्र के अनुसार समूहित करें',
no_tables_found: 'कोई तालिका नहीं मिली',
empty_diagram_description: 'शुरू करने के लिए एक तालिका बनाएं',
no_tables_description:
'अपनी खोज या फ़िल्टर समायोजित करने का प्रयास करें',
clear_filter: 'फ़िल्टर साफ़ करें',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -126,6 +126,8 @@ export const hr: LanguageTranslation = {
'Nema pronađenih tablica koje odgovaraju vašem filteru.',
show_list: 'Prikaži popis tablica',
show_dbml: 'Prikaži DBML uređivač',
all_hidden: 'Sve tablice su skrivene',
show_all: 'Prikaži sve',
table: {
fields: 'Polja',
@@ -517,6 +519,22 @@ export const hr: LanguageTranslation = {
add_relationship: 'Dodaj vezu',
},
canvas: {
all_tables_hidden: 'Sve tablice su skrivene',
show_all_tables: 'Prikaži sve',
},
canvas_filter: {
title: 'Filtriraj tablice',
search_placeholder: 'Pretraži tablice...',
group_by_schema: 'Grupiraj po shemi',
group_by_area: 'Grupiraj po području',
no_tables_found: 'Nisu pronađene tablice',
empty_diagram_description: 'Kreirajte tablicu za početak',
no_tables_description: 'Pokušajte prilagoditi pretragu ili filter',
clear_filter: 'Očisti filter',
},
snap_to_grid_tooltip: 'Priljepljivanje na mrežu (Drži {{key}})',
tool_tips: {

View File

@@ -128,6 +128,8 @@ export const id_ID: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Semua tabel tersembunyi',
show_all: 'Tampilkan semua',
table: {
fields: 'Kolom',
@@ -524,6 +526,22 @@ export const id_ID: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Semua tabel tersembunyi',
show_all_tables: 'Tampilkan semua',
},
canvas_filter: {
title: 'Filter Tabel',
search_placeholder: 'Cari tabel...',
group_by_schema: 'Kelompokkan berdasarkan Skema',
group_by_area: 'Kelompokkan berdasarkan Area',
no_tables_found: 'Tidak ada tabel ditemukan',
empty_diagram_description: 'Buat tabel untuk memulai',
no_tables_description: 'Coba sesuaikan pencarian atau filter Anda',
clear_filter: 'Hapus filter',
},
snap_to_grid_tooltip: 'Snap ke Kisi (Tahan {{key}})',
tool_tips: {

View File

@@ -132,6 +132,8 @@ export const ja: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'すべてのテーブルが非表示です',
show_all: 'すべて表示',
table: {
fields: 'フィールド',
@@ -529,6 +531,22 @@ export const ja: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'すべてのテーブルが非表示です',
show_all_tables: 'すべて表示',
},
canvas_filter: {
title: 'テーブルをフィルター',
search_placeholder: 'テーブルを検索...',
group_by_schema: 'スキーマでグループ化',
group_by_area: 'エリアでグループ化',
no_tables_found: 'テーブルが見つかりません',
empty_diagram_description: 'テーブルを作成して開始',
no_tables_description: '検索またはフィルターを調整してください',
clear_filter: 'フィルターをクリア',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -128,6 +128,8 @@ export const ko_KR: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: '모든 테이블이 숨겨져 있습니다',
show_all: '모두 표시',
table: {
fields: '필드',
@@ -521,6 +523,22 @@ export const ko_KR: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: '모든 테이블이 숨겨져 있습니다',
show_all_tables: '모두 표시',
},
canvas_filter: {
title: '테이블 필터',
search_placeholder: '테이블 검색...',
group_by_schema: '스키마별 그룹화',
group_by_area: '영역별 그룹화',
no_tables_found: '테이블을 찾을 수 없습니다',
empty_diagram_description: '시작하려면 테이블을 만드세요',
no_tables_description: '검색 또는 필터를 조정해 보세요',
clear_filter: '필터 지우기',
},
snap_to_grid_tooltip: '그리드에 맞추기 ({{key}}를 누른채 유지)',
tool_tips: {

View File

@@ -131,6 +131,8 @@ export const mr: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'सर्व टेबल्स लपवलेले आहेत',
show_all: 'सर्व दाखवा',
table: {
fields: 'फील्ड्स',
@@ -533,6 +535,23 @@ export const mr: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'सर्व टेबल्स लपवलेले आहेत',
show_all_tables: 'सर्व दाखवा',
},
canvas_filter: {
title: 'टेबल्स फिल्टर करा',
search_placeholder: 'टेबल्स शोधा...',
group_by_schema: 'स्कीमानुसार गट करा',
group_by_area: 'क्षेत्रानुसार गट करा',
no_tables_found: 'कोणतेही टेबल सापडले नाही',
empty_diagram_description: 'सुरू करण्यासाठी टेबल तयार करा',
no_tables_description:
'तुमची शोध किंवा फिल्टर समायोजित करण्याचा प्रयत्न करा',
clear_filter: 'फिल्टर साफ करा',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -129,6 +129,8 @@ export const ne: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'सबै तालिकाहरू लुकेका छन्',
show_all: 'सबै देखाउनुहोस्',
table: {
fields: 'क्षेत्रहरू',
@@ -527,6 +529,23 @@ export const ne: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'सबै तालिकाहरू लुकेका छन्',
show_all_tables: 'सबै देखाउनुहोस्',
},
canvas_filter: {
title: 'तालिकाहरू फिल्टर गर्नुहोस्',
search_placeholder: 'तालिकाहरू खोज्नुहोस्...',
group_by_schema: 'स्कीमा अनुसार समूह गर्नुहोस्',
group_by_area: 'क्षेत्र अनुसार समूह गर्नुहोस्',
no_tables_found: 'कुनै तालिका भेटिएन',
empty_diagram_description: 'सुरु गर्न तालिका बनाउनुहोस्',
no_tables_description:
'तपाईंको खोज वा फिल्टर समायोजन गर्ने प्रयास गर्नुहोस्',
clear_filter: 'फिल्टर हटाउनुहोस्',
},
snap_to_grid_tooltip: 'ग्रिडमा स्न्याप गर्नुहोस् ({{key}} थिच्नुहोस)',
tool_tips: {

View File

@@ -129,6 +129,8 @@ export const pt_BR: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Todas as tabelas estão ocultas',
show_all: 'Mostrar tudo',
table: {
fields: 'Campos',
@@ -526,6 +528,22 @@ export const pt_BR: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Todas as tabelas estão ocultas',
show_all_tables: 'Mostrar tudo',
},
canvas_filter: {
title: 'Filtrar Tabelas',
search_placeholder: 'Pesquisar tabelas...',
group_by_schema: 'Agrupar por Esquema',
group_by_area: 'Agrupar por Área',
no_tables_found: 'Nenhuma tabela encontrada',
empty_diagram_description: 'Crie uma tabela para começar',
no_tables_description: 'Tente ajustar sua pesquisa ou filtro',
clear_filter: 'Limpar filtro',
},
// TODO: Add translations
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -126,6 +126,8 @@ export const ru: LanguageTranslation = {
'Таблицы не найдены, соответствующие вашему фильтру.',
show_list: 'Переключиться на список таблиц',
show_dbml: 'Переключиться на редактор DBML',
all_hidden: 'Все таблицы скрыты',
show_all: 'Показать все',
table: {
fields: 'Поля',
@@ -521,6 +523,22 @@ export const ru: LanguageTranslation = {
add_relationship: 'Добавить связь',
},
canvas: {
all_tables_hidden: 'Все таблицы скрыты',
show_all_tables: 'Показать все',
},
canvas_filter: {
title: 'Фильтр таблиц',
search_placeholder: 'Поиск таблиц...',
group_by_schema: 'Группировать по схеме',
group_by_area: 'Группировать по области',
no_tables_found: 'Таблицы не найдены',
empty_diagram_description: 'Создайте таблицу, чтобы начать',
no_tables_description: 'Попробуйте изменить поиск или фильтр',
clear_filter: 'Очистить фильтр',
},
copy_to_clipboard: 'Скопировать в буфер обмена',
copied: 'Скопировано!',
snap_to_grid_tooltip: 'Выравнивание по сетке (Удерживайте {{key}})',

View File

@@ -129,6 +129,8 @@ export const te: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'అన్ని పట్టికలు దాచబడ్డాయి',
show_all: 'అన్ని చూపించు',
table: {
fields: 'ఫీల్డులు',
@@ -530,6 +532,23 @@ export const te: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'అన్ని పట్టికలు దాచబడ్డాయి',
show_all_tables: 'అన్ని చూపించు',
},
canvas_filter: {
title: 'పట్టికలను ఫిల్టర్ చేయండి',
search_placeholder: 'పట్టికలను శోధించండి...',
group_by_schema: 'స్కీమా ద్వారా గ్రూప్ చేయండి',
group_by_area: 'ప్రాంతం ద్వారా గ్రూప్ చేయండి',
no_tables_found: 'పట్టికలు కనుగొనబడలేదు',
empty_diagram_description: 'ప్రారంభించడానికి పట్టికను సృష్టించండి',
no_tables_description:
'మీ శోధన లేదా ఫిల్టర్‌ను సర్దుబాటు చేయడానికి ప్రయత్నించండి',
clear_filter: 'ఫిల్టర్ క్లియర్ చేయండి',
},
// TODO: Translate
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -128,6 +128,8 @@ export const tr: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Tüm tablolar gizli',
show_all: 'Tümünü göster',
table: {
fields: 'Alanlar',
@@ -514,6 +516,23 @@ export const tr: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Tüm tablolar gizli',
show_all_tables: 'Tümünü göster',
},
canvas_filter: {
title: 'Tabloları Filtrele',
search_placeholder: 'Tablo ara...',
group_by_schema: 'Şemaya Göre Grupla',
group_by_area: 'Alana Göre Grupla',
no_tables_found: 'Tablo bulunamadı',
empty_diagram_description: 'Başlamak için bir tablo oluşturun',
no_tables_description:
'Aramanızı veya filtrenizi ayarlamayı deneyin',
clear_filter: 'Filtreyi temizle',
},
// TODO: Translate
snap_to_grid_tooltip: 'Snap to Grid (Hold {{key}})',

View File

@@ -127,6 +127,8 @@ export const uk: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Всі таблиці приховані',
show_all: 'Показати все',
table: {
fields: 'Поля',
@@ -521,6 +523,22 @@ export const uk: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Всі таблиці приховані',
show_all_tables: 'Показати все',
},
canvas_filter: {
title: 'Фільтрувати таблиці',
search_placeholder: 'Пошук таблиць...',
group_by_schema: 'Групувати за схемою',
group_by_area: 'Групувати за областю',
no_tables_found: 'Таблиці не знайдено',
empty_diagram_description: 'Створіть таблицю, щоб почати',
no_tables_description: 'Спробуйте налаштувати пошук або фільтр',
clear_filter: 'Очистити фільтр',
},
snap_to_grid_tooltip: 'Вирівнювати за сіткою (Отримуйте {{key}})',
tool_tips: {

View File

@@ -128,6 +128,8 @@ export const vi: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: 'Tất cả bảng đã bị ẩn',
show_all: 'Hiển thị tất cả',
table: {
fields: 'Trường',
@@ -522,6 +524,23 @@ export const vi: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: 'Tất cả bảng đã bị ẩn',
show_all_tables: 'Hiển thị tất cả',
},
canvas_filter: {
title: 'Lọc bảng',
search_placeholder: 'Tìm kiếm bảng...',
group_by_schema: 'Nhóm theo Schema',
group_by_area: 'Nhóm theo Khu vực',
no_tables_found: 'Không tìm thấy bảng',
empty_diagram_description: 'Tạo bảng để bắt đầu',
no_tables_description:
'Thử điều chỉnh tìm kiếm hoặc bộ lọc của bạn',
clear_filter: 'Xóa bộ lọc',
},
snap_to_grid_tooltip: 'Căn lưới (Giữ phím {{key}})',
tool_tips: {

View File

@@ -125,6 +125,8 @@ export const zh_CN: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: '所有表格已隐藏',
show_all: '显示全部',
table: {
fields: '字段',
@@ -516,6 +518,22 @@ export const zh_CN: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: '所有表格已隐藏',
show_all_tables: '显示全部',
},
canvas_filter: {
title: '筛选表格',
search_placeholder: '搜索表格...',
group_by_schema: '按模式分组',
group_by_area: '按区域分组',
no_tables_found: '未找到表格',
empty_diagram_description: '创建表格以开始',
no_tables_description: '尝试调整您的搜索或筛选',
clear_filter: '清除筛选',
},
snap_to_grid_tooltip: '对齐到网格(按住 {{key}}',
tool_tips: {

View File

@@ -125,6 +125,8 @@ export const zh_TW: LanguageTranslation = {
// TODO: Translate
show_list: 'Show Table List',
show_dbml: 'Show DBML Editor',
all_hidden: '所有表格已隱藏',
show_all: '顯示全部',
table: {
fields: '欄位',
@@ -516,6 +518,22 @@ export const zh_TW: LanguageTranslation = {
add_relationship: 'Add Relationship', // TODO: Translate
},
canvas: {
all_tables_hidden: '所有表格已隱藏',
show_all_tables: '顯示全部',
},
canvas_filter: {
title: '篩選表格',
search_placeholder: '搜尋表格...',
group_by_schema: '依架構分組',
group_by_area: '依區域分組',
no_tables_found: '找不到表格',
empty_diagram_description: '建立表格以開始',
no_tables_description: '嘗試調整您的搜尋或篩選',
clear_filter: '清除篩選',
},
snap_to_grid_tooltip: '對齊網格(按住 {{key}}',
tool_tips: {

View File

@@ -5,7 +5,7 @@ import React, {
useEffect,
useRef,
} from 'react';
import { X, Search, Database, Table, Funnel, Box } from 'lucide-react';
import { X, Search, Database, Table, Box, SearchX } from 'lucide-react';
import { useChartDB } from '@/hooks/use-chartdb';
import { useTranslation } from 'react-i18next';
import { Button } from '@/components/button/button';
@@ -154,6 +154,23 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
return result;
}, [treeData, searchQuery]);
// Check if there are no tables to show
const hasNoTables = useMemo(() => {
const totalTables = filteredTreeData.reduce(
(sum, node) => sum + (node.children?.length ?? 0),
0
);
return totalTables === 0;
}, [filteredTreeData]);
// Check if the diagram is completely empty (no tables at all)
const isDiagramEmpty = useMemo(() => tables.length === 0, [tables.length]);
// Clear search query only (preserves user's hide/show selections)
const handleClearFilter = useCallback(() => {
setSearchQuery('');
}, []);
// Render actions with proper memoization for performance
const renderActions = useCallback(
(node: TreeNode<NodeType, NodeContext>) => (
@@ -269,9 +286,9 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
{/* Header */}
<div className="flex items-center justify-between rounded-t-lg border-b px-2 py-1">
<div className="flex items-center gap-2">
<Funnel className="size-3.5 text-muted-foreground md:size-4" />
<Search className="size-3.5 text-muted-foreground md:size-4" />
<h2 className="text-sm font-medium">
{t('canvas_filter.title', 'Filter Tables')}{' '}
{t('canvas_filter.title')}{' '}
</h2>
<span className="text-xs text-muted-foreground">
({openFilterShortcut})
@@ -293,10 +310,7 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
<Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
<Input
ref={searchInputRef}
placeholder={t(
'canvas_filter.search_placeholder',
'Search tables...'
)}
placeholder={t('canvas_filter.search_placeholder')}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="h-full pl-9"
@@ -316,28 +330,58 @@ export const CanvasFilter: React.FC<CanvasFilterProps> = ({ onClose }) => {
>
<ToggleGroupItem value="schema" className="flex-1 text-xs">
<Database className="mr-1.5 size-3.5" />
{t('canvas_filter.group_by_schema', 'Group by Schema')}
{t('canvas_filter.group_by_schema')}
</ToggleGroupItem>
<ToggleGroupItem value="area" className="flex-1 text-xs">
<Box className="mr-1.5 size-3.5" />
{t('canvas_filter.group_by_area', 'Group by Area')}
{t('canvas_filter.group_by_area')}
</ToggleGroupItem>
</ToggleGroup>
</div>
{/* Table Tree */}
<ScrollArea className="flex-1 rounded-b-lg" type="auto">
<TreeView
data={filteredTreeData}
onNodeClick={handleNodeClick}
renderActionsComponent={renderActions}
defaultFolderIcon={groupingMode === 'area' ? Box : Database}
defaultIcon={Table}
expanded={expanded}
setExpanded={setExpanded}
className="py-2"
disableCache={true}
/>
{hasNoTables ? (
<div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center">
<SearchX className="size-10 text-muted-foreground/50" />
<div className="space-y-1">
<p className="text-sm font-medium text-muted-foreground">
{t('canvas_filter.no_tables_found')}
</p>
<p className="text-xs text-muted-foreground/70">
{isDiagramEmpty
? t(
'canvas_filter.empty_diagram_description'
)
: t('canvas_filter.no_tables_description')}
</p>
</div>
{!isDiagramEmpty && (
<Button
variant="outline"
size="sm"
onClick={handleClearFilter}
className="mt-2"
>
{t('canvas_filter.clear_filter')}
</Button>
)}
</div>
) : (
<TreeView
data={filteredTreeData}
onNodeClick={handleNodeClick}
renderActionsComponent={renderActions}
defaultFolderIcon={
groupingMode === 'area' ? Box : Database
}
defaultIcon={Table}
expanded={expanded}
setExpanded={setExpanded}
className="py-2"
disableCache={true}
/>
)}
</ScrollArea>
</div>
);

View File

@@ -45,7 +45,13 @@ import {
} from './table-node/table-node-field';
import { Toolbar } from './toolbar/toolbar';
import { useToast } from '@/components/toast/use-toast';
import { Pencil, AlertTriangle, Magnet, Highlighter } from 'lucide-react';
import {
Pencil,
Magnet,
AlertTriangle,
Highlighter,
EyeOff,
} from 'lucide-react';
import { Button } from '@/components/button/button';
import { useLayout } from '@/hooks/use-layout';
import { useBreakpoint } from '@/hooks/use-breakpoint';
@@ -314,7 +320,12 @@ export const Canvas: React.FC<CanvasProps> = ({ initialTables }) => {
closeRelationshipPopover,
events: canvasEvents,
} = useCanvas();
const { filter, loading: filterLoading } = useDiagramFilter();
const {
filter,
loading: filterLoading,
hasActiveFilter,
resetFilter,
} = useDiagramFilter();
const { checkIfNewTable } = useDiff();
const shouldForceShowTable = useCallback(
@@ -1415,6 +1426,23 @@ export const Canvas: React.FC<CanvasProps> = ({ initialTables }) => {
[overlapGraph]
);
// Check if all tables are hidden due to filtering
// Derived from filter state directly (not nodes) for better performance
const allTablesHiddenByFilter = useMemo(() => {
if (!hasActiveFilter || tables.length === 0 || filterLoading) {
return false;
}
// Check if any table passes the filter
const visibleTableCount = tables.filter((table) =>
filterTable({
table: { id: table.id, schema: table.schema },
filter,
options: { defaultSchema: defaultSchemas[databaseType] },
})
).length;
return visibleTableCount === 0;
}, [hasActiveFilter, tables, filter, databaseType, filterLoading]);
const pulseOverlappingTables = useCallback(() => {
setHighlightOverlappingTables(true);
setTimeout(() => setHighlightOverlappingTables(false), 600);
@@ -1801,6 +1829,24 @@ export const Canvas: React.FC<CanvasProps> = ({ initialTables }) => {
gap={16}
size={1}
/>
{/* Empty state when all tables are hidden by filter */}
{allTablesHiddenByFilter && (
<div className="pointer-events-none absolute inset-0 z-10 flex items-center justify-center">
<div className="pointer-events-auto flex items-center gap-3 rounded-lg border bg-background/90 px-4 py-3 shadow-sm backdrop-blur-sm">
<EyeOff className="size-5 text-muted-foreground" />
<span className="text-sm text-muted-foreground">
{t('canvas.all_tables_hidden')}
</span>
<Button
variant="outline"
size="sm"
onClick={() => resetFilter()}
>
{t('canvas.show_all_tables')}
</Button>
</div>
</div>
)}
{showFilter ? (
<CanvasFilter onClose={() => setShowFilter(false)} />
) : null}

View File

@@ -3,7 +3,7 @@ import { Card, CardContent } from '@/components/card/card';
import {
ZoomIn,
ZoomOut,
Funnel,
Search,
Redo,
Undo,
Scan,
@@ -104,7 +104,7 @@ export const Toolbar: React.FC<ToolbarProps> = ({ readonly }) => {
}
)}
>
<Funnel />
<Search />
</ToolbarButton>
</span>
</TooltipTrigger>

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useMemo } from 'react';
import { TableList } from './table-list/table-list';
import { Button } from '@/components/button/button';
import { Table, View, X } from 'lucide-react';
import { Table, View, X, EyeOff } from 'lucide-react';
import { Input } from '@/components/input/input';
import type { DBTable } from '@/lib/domain/db-table';
import { useChartDB } from '@/hooks/use-chartdb';
@@ -22,7 +22,8 @@ export interface TablesSectionProps {}
export const TablesSection: React.FC<TablesSectionProps> = () => {
const { createTable, tables, databaseType, readonly } = useChartDB();
const { filter, schemasDisplayed } = useDiagramFilter();
const { filter, schemasDisplayed, hasActiveFilter, resetFilter } =
useDiagramFilter();
const { openTableSchemaDialog } = useDialog();
const viewport = useViewport();
const { t } = useTranslation();
@@ -31,31 +32,41 @@ export const TablesSection: React.FC<TablesSectionProps> = () => {
const { showDBViews } = useLocalConfig();
const filterInputRef = React.useRef<HTMLInputElement>(null);
// First, filter tables by the diagram filter (schemas/tables visibility)
// This is computed once and reused for both filteredTables and allTablesHiddenByDiagramFilter
const tablesFilteredByDiagram = useMemo(
() =>
tables.filter((table) =>
filterTable({
table: { id: table.id, schema: table.schema },
filter,
options: { defaultSchema: defaultSchemas[databaseType] },
})
),
[tables, filter, databaseType]
);
// Check if all tables are hidden by the diagram filter (not the text search)
const allTablesHiddenByDiagramFilter = useMemo(() => {
if (!hasActiveFilter || tables.length === 0) {
return false;
}
return tablesFilteredByDiagram.length === 0;
}, [hasActiveFilter, tables.length, tablesFilteredByDiagram.length]);
// Apply additional filters (text search and views) on top of diagram-filtered tables
const filteredTables = useMemo(() => {
const filterTableName: (table: DBTable) => boolean = (table) =>
!filterText?.trim?.() ||
table.name.toLowerCase().includes(filterText.toLowerCase());
const filterTables: (table: DBTable) => boolean = (table) =>
filterTable({
table: {
id: table.id,
schema: table.schema,
},
filter,
options: {
defaultSchema: defaultSchemas[databaseType],
},
});
const filterViews: (table: DBTable) => boolean = (table) =>
showDBViews ? true : !table.isView;
return tables
.filter(filterTables)
return tablesFilteredByDiagram
.filter(filterTableName)
.filter(filterViews);
}, [tables, filterText, filter, databaseType, showDBViews]);
}, [tablesFilteredByDiagram, filterText, showDBViews]);
const getCenterLocation = useCallback(() => {
const padding = 80;
@@ -173,6 +184,23 @@ export const TablesSection: React.FC<TablesSectionProps> = () => {
</ButtonWithAlternatives>
) : null}
</div>
{/* Indicator when all tables are hidden by diagram filter */}
{allTablesHiddenByDiagramFilter && (
<div className="mb-2 flex items-center gap-2 rounded-md border bg-muted/50 px-3 py-2">
<EyeOff className="size-4 text-muted-foreground" />
<span className="flex-1 text-xs text-muted-foreground">
{t('side_panel.tables_section.all_hidden')}
</span>
<Button
variant="outline"
size="sm"
className="h-6 px-2 text-xs"
onClick={() => resetFilter()}
>
{t('side_panel.tables_section.show_all')}
</Button>
</div>
)}
<div className="flex flex-1 flex-col overflow-hidden">
<ScrollArea className="h-full">
{tables.length === 0 ? (