diff --git a/client/src/components/Packing/PackingListPanelCategoryGroup.tsx b/client/src/components/Packing/PackingListPanelCategoryGroup.tsx index 78a4761b1..c911c8c1b 100644 --- a/client/src/components/Packing/PackingListPanelCategoryGroup.tsx +++ b/client/src/components/Packing/PackingListPanelCategoryGroup.tsx @@ -4,7 +4,7 @@ import { useToast } from '../shared/Toast' import { useTranslation } from '../../i18n' import { Trash2, Plus, ChevronDown, ChevronRight, - X, Pencil, Check, MoreHorizontal, CheckCheck, RotateCcw, UserPlus, + X, Pencil, Check, MoreHorizontal, CheckCheck, RotateCcw, UserPlus, GripVertical, } from 'lucide-react' import type { PackingItem, PackingBag } from '../../types' import { katColor } from './packingListPanel.helpers' @@ -38,6 +38,18 @@ interface KategorieGruppeProps { // Off while the list is shown A-Z: a drop there would move an item in an order // the reader cannot see. reorderable?: boolean + // Moving the whole category among the others, wired by the list, which sees every + // card. `active` is true while any category is being dragged, so a dragged ITEM + // passing over a card is left to the item rows. + categoryDrag?: { + active: boolean + isDragging: boolean + isOver: boolean + onStart: () => void + onOver: () => void + onEnd: () => void + onDrop: () => void + } // Three-tier sharing (#858) — threaded down to each item's share control. currentUserId?: number onSetSharing?: (id: number, visibility: 'common' | 'personal' | 'shared', recipientIds: number[]) => void @@ -46,7 +58,7 @@ interface KategorieGruppeProps { onLeave?: (id: number, userId: number) => void } -export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRename, onDeleteAll, onDeleteItem, onAddItem, assignees, tripMembers, onSetAssignees, bagTrackingEnabled, bags, onCreateBag, canEdit = true, allItems, onReorder, reorderable = true, currentUserId, onSetSharing, onClone, onJoin, onLeave }: KategorieGruppeProps) { +export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRename, onDeleteAll, onDeleteItem, onAddItem, assignees, tripMembers, onSetAssignees, bagTrackingEnabled, bags, onCreateBag, canEdit = true, allItems, onReorder, reorderable = true, categoryDrag, currentUserId, onSetSharing, onClone, onJoin, onLeave }: KategorieGruppeProps) { const [offen, setOffen] = useState(true) const [dragId, setDragId] = useState(null) const [overId, setOverId] = useState(null) @@ -120,8 +132,9 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen return ( // Shaped after the phone's category card: a tinted head band carrying the // name as written, the count, and the fold arrow on the far right. -
+
+ {editingName && canEdit ? ( @@ -344,6 +357,48 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen ))}
)} +
+ ) +} + +type CategoryDrag = NonNullable + +/** + * The card, and while a category is dragged its drop target. Captured, because + * the item rows stop their own drops from bubbling. + */ +function CategoryDropFrame({ drag, children }: { drag?: CategoryDrag; children: React.ReactNode }) { + const target = drag?.active ? drag : null + return ( +
{ e.preventDefault(); e.dataTransfer.dropEffect = 'move'; target.onOver() }) : undefined} + onDropCapture={target ? (e => { e.preventDefault(); e.stopPropagation(); target.onDrop() }) : undefined} + style={{ + marginBottom: 6, background: 'var(--bg-card)', borderRadius: 16, border: '1px solid var(--border-secondary)', overflow: 'visible', + opacity: drag?.isDragging ? 0.4 : 1, + boxShadow: drag?.isOver ? '0 0 0 2px var(--accent)' : 'none', + transition: 'opacity 0.15s, box-shadow 0.15s', + }} + > + {children}
) } + +/** The handle a category is dragged by, in the head band before its colour dot. */ +function CategoryGrip({ drag, kategorie, label }: { drag?: CategoryDrag; kategorie: string; label: string }) { + if (!drag) return null + return ( + { e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', kategorie); drag.onStart() }} + onDragEnd={drag.onEnd} + style={{ cursor: 'grab', display: 'flex', alignItems: 'center', color: 'var(--text-faint)', flexShrink: 0, marginInlineStart: -6 }} + > + + + ) +} diff --git a/client/src/components/Packing/PackingListPanelList.tsx b/client/src/components/Packing/PackingListPanelList.tsx index 946da4e37..ac9c950f9 100644 --- a/client/src/components/Packing/PackingListPanelList.tsx +++ b/client/src/components/Packing/PackingListPanelList.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import type { PackingState } from './usePackingListPanel' import { KategorieGruppe } from './PackingListPanelCategoryGroup' import EmptyState from '../shared/EmptyState' @@ -7,8 +8,13 @@ export function PackingList(S: PackingState) { items, gruppiert, t, tripId, allCategories, handleRenameCategory, handleDeleteCategory, handleDeleteItem, handleAddItemToCategory, categoryAssignees, tripMembers, handleSetAssignees, bagTrackingEnabled, bags, handleCreateBagByName, canEdit, reorderPackingItems, - currentUserId, handleSetSharing, handleCloneItem, handleJoinItem, handleLeaveItem, sort, + currentUserId, handleSetSharing, handleCloneItem, handleJoinItem, handleLeaveItem, sort, moveCategory, } = S + // Which category is being dragged and which card it is over. Held here, above + // the cards, because the drag starts in one card and ends in another. + const [draggedCategory, setDraggedCategory] = useState(null) + const [overCategory, setOverCategory] = useState(null) + const endCategoryDrag = () => { setDraggedCategory(null); setOverCategory(null) } return (
{items.length === 0 ? ( @@ -40,6 +46,18 @@ export function PackingList(S: PackingState) { allItems={items} onReorder={(orderedIds) => reorderPackingItems(tripId, orderedIds)} reorderable={sort === 'manual'} + categoryDrag={canEdit ? { + active: draggedCategory !== null, + isDragging: draggedCategory === kat, + isOver: overCategory === kat && draggedCategory !== null && draggedCategory !== kat, + onStart: () => { setDraggedCategory(kat); setOverCategory(null) }, + onOver: () => setOverCategory(kat), + onEnd: endCategoryDrag, + onDrop: () => { + if (draggedCategory !== null) moveCategory(draggedCategory, kat) + endCategoryDrag() + }, + } : undefined} currentUserId={currentUserId} onSetSharing={handleSetSharing} onClone={handleCloneItem} diff --git a/client/src/components/Packing/packingListModel.test.ts b/client/src/components/Packing/packingListModel.test.ts index 2388109ca..2d397d6b5 100644 --- a/client/src/components/Packing/packingListModel.test.ts +++ b/client/src/components/Packing/packingListModel.test.ts @@ -6,6 +6,7 @@ import { groupPackingItems, isLastCustomItemInCategory, isPackingPlaceholder, + itemOrderWithCategoryMoved, packingCategoryOrder, packingItemWeight, packingProgress, @@ -14,7 +15,7 @@ import { } from './packingListModel'; import { PACKING_PLACEHOLDER_NAME } from './packingListPanel.constants'; -// FE-PACK-MODEL-001 to FE-PACK-MODEL-008 +// FE-PACK-MODEL-001 to FE-PACK-MODEL-009 const shared = buildPackingItem({ id: 1, name: 'Tent', category: 'Camp', checked: 1 }); const mine = buildPackingItem({ id: 2, name: 'Socks', category: 'Clothes', is_private: 1 }); @@ -76,4 +77,28 @@ describe('packingListModel', () => { expect(isPackingPlaceholder({ name: PACKING_PLACEHOLDER_NAME })).toBe(true); expect(isPackingPlaceholder({ name: 'Tent' })).toBe(false); }); + it('FE-PACK-MODEL-009: moving a category moves its items as a block, the other view stays as it was', () => { + const day1 = buildPackingItem({ id: 11, name: 'Shirt', category: 'Day 1' }); + const day2a = buildPackingItem({ id: 12, name: 'Shorts', category: 'Day 2' }); + const own = buildPackingItem({ id: 13, name: 'Pills', category: 'Day 2', is_private: 1 }); + const day3 = buildPackingItem({ id: 14, name: 'Jacket', category: 'Day 3' }); + const day2b = buildPackingItem({ id: 15, name: 'Hat', category: 'Day 2' }); + const items = [day1, day2a, own, day3, day2b]; + const common = packingViewItems(items, 'common'); + + const order = itemOrderWithCategoryMoved(items, common, 'Day 3', 'Day 1', 'Other'); + expect(order).toEqual([14, 11, 12, 15, 13]); + const moved = (order ?? []).flatMap((id) => items.filter((i) => i.id === id)); + expect(packingCategoryOrder(packingViewItems(moved, 'common'), 'Other')).toEqual(['Day 3', 'Day 1', 'Day 2']); + expect(packingCategoryOrder(packingViewItems(moved, 'personal'), 'Other')).toEqual(['Day 2']); + + expect(itemOrderWithCategoryMoved(items, common, 'Day 1', 'Day 3', 'Other')).toEqual([12, 15, 14, 11, 13]); + }); + + it('FE-PACK-MODEL-009b: nothing moves for the same category or one the view does not show', () => { + const items = [shared, stove, loose]; + expect(itemOrderWithCategoryMoved(items, items, 'Camp', 'Camp', 'Other')).toBeNull(); + expect(itemOrderWithCategoryMoved(items, items, 'Clothes', 'Camp', 'Other')).toBeNull(); + expect(itemOrderWithCategoryMoved(items, items, 'Other', 'Camp', 'Other')).toEqual([4, 1, 3]); + }); }); diff --git a/client/src/components/Packing/packingListModel.ts b/client/src/components/Packing/packingListModel.ts index 39b195958..774d56b6c 100644 --- a/client/src/components/Packing/packingListModel.ts +++ b/client/src/components/Packing/packingListModel.ts @@ -25,6 +25,34 @@ export function packingCategoryOrder(viewItems: PackingItem[], defaultCategory: return seen; } +/** + * The whole list's item order with category `from` moved to where `to` stands. + * + * A category has no position of its own: it shows where its first item is. So a + * category is moved by moving its items, as one block, through the item reorder + * the list already has; nothing new is stored, and the order reaches every device + * the way a dragged item does. The view's items come first, grouped in the new + * order with each group in its own order; the other view's items follow in theirs, + * which leaves that view's categories as they were. Null when nothing would move. + */ +export function itemOrderWithCategoryMoved( + items: PackingItem[], + viewItems: PackingItem[], + from: string, + to: string, + defaultCategory: string +): number[] | null { + const order = packingCategoryOrder(viewItems, defaultCategory); + const fromIndex = order.indexOf(from); + const toIndex = order.indexOf(to); + if (fromIndex < 0 || toIndex < 0 || fromIndex === toIndex) return null; + order.splice(fromIndex, 1); + order.splice(toIndex, 0, from); + const inView = new Set(viewItems.map((i) => i.id)); + const grouped = order.flatMap((cat) => viewItems.filter((i) => (i.category || defaultCategory) === cat)); + return [...grouped, ...items.filter((i) => !inView.has(i.id))].map((i) => i.id); +} + export function packingStatusFiltered(items: PackingItem[], status: PackingStatusFilter): PackingItem[] { if (status === 'open') return items.filter((i) => !i.checked); if (status === 'done') return items.filter((i) => !!i.checked); diff --git a/client/src/components/Packing/usePackingListPanel.ts b/client/src/components/Packing/usePackingListPanel.ts index 579784529..0b1a3f4f3 100644 --- a/client/src/components/Packing/usePackingListPanel.ts +++ b/client/src/components/Packing/usePackingListPanel.ts @@ -8,7 +8,7 @@ import { tripsApi } from '../../api/client' import { useAddonStore } from '../../store/addonStore' import type { PackingItem } from '../../types' import { parseImportLines, sortItemsByName } from './packingListPanel.helpers' -import { groupPackingItems, packingCategoryOrder, packingProgress, packingViewItems } from './packingListModel' +import { groupPackingItems, itemOrderWithCategoryMoved, packingCategoryOrder, packingProgress, packingViewItems } from './packingListModel' import { usePackingBags } from './usePackingBags' import { usePackingCategoryAssignees } from './usePackingCategoryAssignees' import { usePackingImport } from './usePackingImport' @@ -106,6 +106,13 @@ export function usePackingList({ tripId, items, openImportSignal = 0, addCategor const { checked: abgehakt, pct: fortschritt } = packingProgress(viewItems) + // A category dropped onto another takes its place; the slice reorders + // optimistically and rolls back with a toast of its own. + const moveCategory = (from: string, to: string) => { + const orderedIds = itemOrderWithCategoryMoved(items, viewItems, from, to, t('packing.defaultCategory')) + if (orderedIds) void reorderPackingItems(tripId, orderedIds) + } + const { addingCategory, setAddingCategory, newCategoryName: newCatName, setNewCategoryName: setNewCatName, addItemToCategory: handleAddItemToCategory, deleteItem: handleDeleteItem, addNewCategory: handleAddNewCategory, @@ -213,7 +220,7 @@ export function usePackingList({ tripId, items, openImportSignal = 0, addCategor handleSetSharing, handleCloneItem, handleJoinItem, handleLeaveItem, tripId, items, inlineHeader, t, canEdit, isAdmin, font, reorderPackingItems, filter, setFilter, sort, setSort, addingCategory, setAddingCategory, newCatName, setNewCatName, - tripMembers, categoryAssignees, handleSetAssignees, allCategories, gruppiert, abgehakt, fortschritt, + tripMembers, categoryAssignees, handleSetAssignees, allCategories, gruppiert, abgehakt, fortschritt, moveCategory, handleAddItemToCategory, handleAddNewCategory, handleRenameCategory, handleDeleteCategory, handleDeleteItem, handleClearChecked, bagTrackingEnabled, bags, unassignedWeightGrams, serverWeightsFresh, newBagName, setNewBagName, showAddBag, setShowAddBag, showBagModal, setShowBagModal, handleCreateBag, handleCreateBagByName, handleDeleteBag, handleUpdateBag, handleSetBagMembers, diff --git a/shared/src/i18n/ar/packing.ts b/shared/src/i18n/ar/packing.ts index 42c04009f..78c484962 100644 --- a/shared/src/i18n/ar/packing.ts +++ b/shared/src/i18n/ar/packing.ts @@ -111,6 +111,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.few': 'هل أنت متأكد أنك تريد حذف القائمة "{name}" التي تحتوي على {count} عناصر؟', 'packing.confirm.deleteCat.many': 'هل أنت متأكد أنك تريد حذف القائمة "{name}" التي تحتوي على {count} عنصرًا؟', 'packing.defaultCategory': 'أخرى', + 'packing.dragCategory': 'اسحب لإعادة ترتيب الفئة', 'packing.toast.saveError': 'فشل الحفظ', 'packing.toast.deleteError': 'فشل الحذف', 'packing.toast.renameError': 'فشلت إعادة التسمية', diff --git a/shared/src/i18n/az/packing.ts b/shared/src/i18n/az/packing.ts index aaa2e8ebb..e6315d6bf 100644 --- a/shared/src/i18n/az/packing.ts +++ b/shared/src/i18n/az/packing.ts @@ -117,6 +117,7 @@ const packing: TranslationStrings = { '“{name}” siyahısını daxilindəki {count} elementlə birlikdə silmək istədiyinizə əminsiniz?', 'packing.defaultCategory': 'Digər', + 'packing.dragCategory': 'Kateqoriyanı yerini dəyişmək üçün sürüşdürün', 'packing.toast.saveError': 'Yadda saxlamaq mümkün olmadı', 'packing.toast.deleteError': 'Silmək mümkün olmadı', 'packing.toast.renameError': 'Adını dəyişmək mümkün olmadı', diff --git a/shared/src/i18n/br/packing.ts b/shared/src/i18n/br/packing.ts index d773cb681..cd3f387d7 100644 --- a/shared/src/i18n/br/packing.ts +++ b/shared/src/i18n/br/packing.ts @@ -82,6 +82,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Tem certeza de que deseja excluir a lista "{name}" com {count} itens?', 'packing.confirm.deleteCat.one': 'Tem certeza de que deseja excluir a lista "{name}" com {count} item?', 'packing.defaultCategory': 'Outros', + 'packing.dragCategory': 'Arraste para reordenar a categoria', 'packing.toast.saveError': 'Falha ao salvar', 'packing.toast.deleteError': 'Falha ao excluir', 'packing.toast.renameError': 'Falha ao renomear', diff --git a/shared/src/i18n/ca/packing.ts b/shared/src/i18n/ca/packing.ts index a907e5231..4ad02c686 100644 --- a/shared/src/i18n/ca/packing.ts +++ b/shared/src/i18n/ca/packing.ts @@ -70,6 +70,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Segur que vols eliminar la categoria "{name}" amb {count} elements?', 'packing.confirm.deleteCat.one': 'Segur que vols eliminar la categoria "{name}" amb {count} element?', 'packing.defaultCategory': 'Altres', + 'packing.dragCategory': 'Arrossega per reordenar la categoria', 'packing.toast.saveError': "No s'ha pogut desar", 'packing.toast.deleteError': "No s'ha pogut eliminar", 'packing.toast.renameError': "No s'ha pogut canviar el nom", diff --git a/shared/src/i18n/cs/packing.ts b/shared/src/i18n/cs/packing.ts index 10af5f1f5..21f2b9f49 100644 --- a/shared/src/i18n/cs/packing.ts +++ b/shared/src/i18n/cs/packing.ts @@ -90,6 +90,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.one': 'Opravdu chcete smazat seznam „{name}“ s {count} položkou?', 'packing.confirm.deleteCat.few': 'Opravdu chcete smazat seznam „{name}“ s {count} položkami?', 'packing.defaultCategory': 'Ostatní', + 'packing.dragCategory': 'Přetažením změníte pořadí kategorie', 'packing.toast.saveError': 'Uložení se nezdařilo', 'packing.toast.deleteError': 'Smazání se nezdařilo', 'packing.toast.renameError': 'Přejmenování se nezdařilo', diff --git a/shared/src/i18n/de/packing.ts b/shared/src/i18n/de/packing.ts index 4688626b9..e8d737e94 100644 --- a/shared/src/i18n/de/packing.ts +++ b/shared/src/i18n/de/packing.ts @@ -102,6 +102,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Möchtest du die Liste „{name}“ mit {count} Einträgen wirklich löschen?', 'packing.confirm.deleteCat.one': 'Möchtest du die Liste „{name}“ mit {count} Eintrag wirklich löschen?', 'packing.defaultCategory': 'Sonstiges', + 'packing.dragCategory': 'Ziehen, um die Kategorie umzusortieren', 'packing.toast.saveError': 'Fehler beim Speichern', 'packing.toast.deleteError': 'Fehler beim Löschen', 'packing.toast.renameError': 'Fehler beim Umbenennen', diff --git a/shared/src/i18n/en/packing.ts b/shared/src/i18n/en/packing.ts index b61470682..1548c272b 100644 --- a/shared/src/i18n/en/packing.ts +++ b/shared/src/i18n/en/packing.ts @@ -101,6 +101,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Are you sure you want to delete the list "{name}" with {count} items?', 'packing.confirm.deleteCat.one': 'Are you sure you want to delete the list "{name}" with {count} item?', 'packing.defaultCategory': 'Other', + 'packing.dragCategory': 'Drag to reorder the category', 'packing.toast.saveError': 'Failed to save', 'packing.toast.deleteError': 'Failed to delete', 'packing.toast.renameError': 'Failed to rename', diff --git a/shared/src/i18n/es/packing.ts b/shared/src/i18n/es/packing.ts index a75ebca7b..79169e321 100644 --- a/shared/src/i18n/es/packing.ts +++ b/shared/src/i18n/es/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': '¿Seguro que quieres eliminar la lista "{name}" con {count} elementos?', 'packing.confirm.deleteCat.one': '¿Seguro que quieres eliminar la lista "{name}" con {count} elemento?', 'packing.defaultCategory': 'Otros', + 'packing.dragCategory': 'Arrastra para reordenar la categoría', 'packing.toast.saveError': 'No se pudo guardar', 'packing.toast.deleteError': 'No se pudo eliminar', 'packing.toast.renameError': 'No se pudo renombrar', diff --git a/shared/src/i18n/et/packing.ts b/shared/src/i18n/et/packing.ts index 53a28cfca..fbcdc1182 100644 --- a/shared/src/i18n/et/packing.ts +++ b/shared/src/i18n/et/packing.ts @@ -101,6 +101,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Kas soovid kindlasti kustutada nimekirja "{name}" koos {count} esemega?', 'packing.confirm.deleteCat.one': 'Kas soovid kindlasti kustutada nimekirja "{name}" koos {count} esemega?', 'packing.defaultCategory': 'Muu', + 'packing.dragCategory': 'Lohista kategooria järjekorra muutmiseks', 'packing.toast.saveError': 'Salvestamine ebaõnnestus', 'packing.toast.deleteError': 'Kustutamine ebaõnnestus', 'packing.toast.renameError': 'Ümbernimetamine ebaõnnestus', diff --git a/shared/src/i18n/fr/packing.ts b/shared/src/i18n/fr/packing.ts index cbee1132f..ca4c166ac 100644 --- a/shared/src/i18n/fr/packing.ts +++ b/shared/src/i18n/fr/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Voulez-vous vraiment supprimer la liste « {name} » contenant {count} éléments ?', 'packing.confirm.deleteCat.one': 'Voulez-vous vraiment supprimer la liste « {name} » contenant {count} élément ?', 'packing.defaultCategory': 'Autre', + 'packing.dragCategory': 'Faites glisser pour réordonner la catégorie', 'packing.toast.saveError': "Échec de l'enregistrement", 'packing.toast.deleteError': 'Échec de la suppression', 'packing.toast.renameError': 'Échec du renommage', diff --git a/shared/src/i18n/gr/packing.ts b/shared/src/i18n/gr/packing.ts index 9fa1aba9c..3c4bc138e 100644 --- a/shared/src/i18n/gr/packing.ts +++ b/shared/src/i18n/gr/packing.ts @@ -84,6 +84,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Είστε βέβαιοι ότι θέλετε να διαγράψετε τη λίστα "{name}" με {count} στοιχεία;', 'packing.confirm.deleteCat.one': 'Είστε βέβαιοι ότι θέλετε να διαγράψετε τη λίστα "{name}" με {count} στοιχείο;', 'packing.defaultCategory': 'Άλλα', + 'packing.dragCategory': 'Σύρετε για να αλλάξετε τη σειρά της κατηγορίας', 'packing.toast.saveError': 'Αποτυχία αποθήκευσης', 'packing.toast.deleteError': 'Αποτυχία διαγραφής', 'packing.toast.renameError': 'Αποτυχία μετονομασίας', diff --git a/shared/src/i18n/hu/packing.ts b/shared/src/i18n/hu/packing.ts index 465d019eb..2fecec2ac 100644 --- a/shared/src/i18n/hu/packing.ts +++ b/shared/src/i18n/hu/packing.ts @@ -85,6 +85,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.one': 'Biztosan törölni szeretnéd a(z) „{name}” listát a benne lévő {count} elemmel együtt?', 'packing.defaultCategory': 'Egyéb', + 'packing.dragCategory': 'Húzd a kategória átrendezéséhez', 'packing.toast.saveError': 'Nem sikerült menteni', 'packing.toast.deleteError': 'Nem sikerült törölni', 'packing.toast.renameError': 'Nem sikerült átnevezni', diff --git a/shared/src/i18n/id/packing.ts b/shared/src/i18n/id/packing.ts index f554c1225..440b64473 100644 --- a/shared/src/i18n/id/packing.ts +++ b/shared/src/i18n/id/packing.ts @@ -76,6 +76,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': 'Yakin ingin menghapus {count} item yang dicentang?', 'packing.confirm.deleteCat': 'Yakin ingin menghapus daftar "{name}" dengan {count} item?', 'packing.defaultCategory': 'Lainnya', + 'packing.dragCategory': 'Seret untuk mengurutkan ulang kategori', 'packing.toast.saveError': 'Gagal menyimpan', 'packing.toast.deleteError': 'Gagal menghapus', 'packing.toast.renameError': 'Gagal mengganti nama', diff --git a/shared/src/i18n/it/packing.ts b/shared/src/i18n/it/packing.ts index 809d9f35e..a42a55a6c 100644 --- a/shared/src/i18n/it/packing.ts +++ b/shared/src/i18n/it/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Vuoi davvero eliminare la lista "{name}" con {count} elementi?', 'packing.confirm.deleteCat.one': 'Vuoi davvero eliminare la lista "{name}" con {count} elemento?', 'packing.defaultCategory': 'Altro', + 'packing.dragCategory': 'Trascina per riordinare la categoria', 'packing.toast.saveError': 'Impossibile salvare', 'packing.toast.deleteError': 'Impossibile eliminare', 'packing.toast.renameError': 'Impossibile rinominare', diff --git a/shared/src/i18n/ja/packing.ts b/shared/src/i18n/ja/packing.ts index 4a0becad8..888d6db76 100644 --- a/shared/src/i18n/ja/packing.ts +++ b/shared/src/i18n/ja/packing.ts @@ -75,6 +75,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': 'チェック済み{count}件を削除しますか?', 'packing.confirm.deleteCat': 'リスト「{name}」({count}件)を削除してもよろしいですか?', 'packing.defaultCategory': 'その他', + 'packing.dragCategory': 'ドラッグしてカテゴリを並べ替え', 'packing.toast.saveError': '保存に失敗しました', 'packing.toast.deleteError': '削除に失敗しました', 'packing.toast.renameError': '名前の変更に失敗しました', diff --git a/shared/src/i18n/ko/packing.ts b/shared/src/i18n/ko/packing.ts index 6adb5d95b..c38f6f209 100644 --- a/shared/src/i18n/ko/packing.ts +++ b/shared/src/i18n/ko/packing.ts @@ -75,6 +75,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': '체크된 {count}개 항목을 제거할까요?', 'packing.confirm.deleteCat': '"{name}" 목록({count}개 항목)을 삭제하시겠습니까?', 'packing.defaultCategory': '기타', + 'packing.dragCategory': '드래그하여 카테고리 순서 변경', 'packing.toast.saveError': '저장 실패', 'packing.toast.deleteError': '삭제 실패', 'packing.toast.renameError': '이름 변경 실패', diff --git a/shared/src/i18n/nl/packing.ts b/shared/src/i18n/nl/packing.ts index b7595b6c1..11c583b94 100644 --- a/shared/src/i18n/nl/packing.ts +++ b/shared/src/i18n/nl/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Weet je zeker dat je de lijst "{name}" met {count} items wilt verwijderen?', 'packing.confirm.deleteCat.one': 'Weet je zeker dat je de lijst "{name}" met {count} item wilt verwijderen?', 'packing.defaultCategory': 'Overig', + 'packing.dragCategory': 'Sleep om de categorie te verplaatsen', 'packing.toast.saveError': 'Opslaan mislukt', 'packing.toast.deleteError': 'Verwijderen mislukt', 'packing.toast.renameError': 'Hernoemen mislukt', diff --git a/shared/src/i18n/pl/packing.ts b/shared/src/i18n/pl/packing.ts index d4ded8cd5..689cd1311 100644 --- a/shared/src/i18n/pl/packing.ts +++ b/shared/src/i18n/pl/packing.ts @@ -97,6 +97,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.few': 'Czy na pewno chcesz usunąć listę „{name}” z {count} przedmiotami?', 'packing.confirm.deleteCat.many': 'Czy na pewno chcesz usunąć listę „{name}” z {count} przedmiotami?', 'packing.defaultCategory': 'Inne', + 'packing.dragCategory': 'Przeciągnij, aby zmienić kolejność kategorii', 'packing.toast.saveError': 'Nie udało się zapisać', 'packing.toast.deleteError': 'Nie udało się usunąć', 'packing.toast.renameError': 'Nie udało się zmienić nazwy', diff --git a/shared/src/i18n/ru/packing.ts b/shared/src/i18n/ru/packing.ts index d39521cd9..c905ebab7 100644 --- a/shared/src/i18n/ru/packing.ts +++ b/shared/src/i18n/ru/packing.ts @@ -97,6 +97,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.few': 'Вы уверены, что хотите удалить список «{name}» с {count} предметами?', 'packing.confirm.deleteCat.many': 'Вы уверены, что хотите удалить список «{name}» с {count} предметами?', 'packing.defaultCategory': 'Другое', + 'packing.dragCategory': 'Перетащите, чтобы изменить порядок категории', 'packing.toast.saveError': 'Ошибка сохранения', 'packing.toast.deleteError': 'Ошибка удаления', 'packing.toast.renameError': 'Ошибка переименования', diff --git a/shared/src/i18n/sk/packing.ts b/shared/src/i18n/sk/packing.ts index 65fe574fe..d32d29abd 100644 --- a/shared/src/i18n/sk/packing.ts +++ b/shared/src/i18n/sk/packing.ts @@ -109,6 +109,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.one': 'Naozaj chcete odstrániť zoznam „{name}“ s {count} položkou?', 'packing.confirm.deleteCat.few': 'Naozaj chcete odstrániť zoznam „{name}“ s {count} položkami?', 'packing.defaultCategory': 'Ostatné', + 'packing.dragCategory': 'Potiahnutím zmeníte poradie kategórie', 'packing.toast.saveError': 'Uloženie zlyhalo', 'packing.toast.deleteError': 'Odstránenie zlyhalo', 'packing.toast.renameError': 'Premenovanie zlyhalo', diff --git a/shared/src/i18n/sv/packing.ts b/shared/src/i18n/sv/packing.ts index 08f3402a6..c415d0494 100644 --- a/shared/src/i18n/sv/packing.ts +++ b/shared/src/i18n/sv/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': 'Är du säker på att du vill ta bort listan "{name}" med {count} föremål?', 'packing.confirm.deleteCat.one': 'Är du säker på att du vill ta bort listan "{name}" med {count} föremål?', 'packing.defaultCategory': 'Övrigt', + 'packing.dragCategory': 'Dra för att ändra kategorins ordning', 'packing.toast.saveError': 'Kunde inte spara', 'packing.toast.deleteError': 'Kunde inte ta bort', 'packing.toast.renameError': 'Kunde inte döpa om', diff --git a/shared/src/i18n/th/packing.ts b/shared/src/i18n/th/packing.ts index 74f2509d0..2e8516bca 100644 --- a/shared/src/i18n/th/packing.ts +++ b/shared/src/i18n/th/packing.ts @@ -93,6 +93,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': 'คุณแน่ใจหรือไม่ว่าต้องการลบรายการที่ทำเครื่องหมายแล้ว {count} รายการ', 'packing.confirm.deleteCat': 'คุณแน่ใจหรือไม่ว่าต้องการลบรายการ "{name}" ที่มี {count} รายการ', 'packing.defaultCategory': 'อื่นๆ', + 'packing.dragCategory': 'ลากเพื่อจัดลำดับหมวดหมู่ใหม่', 'packing.toast.saveError': 'บันทึกไม่สำเร็จ', 'packing.toast.deleteError': 'ลบไม่สำเร็จ', 'packing.toast.renameError': 'เปลี่ยนชื่อไม่สำเร็จ', diff --git a/shared/src/i18n/tr/packing.ts b/shared/src/i18n/tr/packing.ts index d0266cb68..0b61e54d2 100644 --- a/shared/src/i18n/tr/packing.ts +++ b/shared/src/i18n/tr/packing.ts @@ -83,6 +83,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat': '"{name}" listesini {count} öğesiyle birlikte silmek istediğinizden emin misiniz?', 'packing.confirm.deleteCat.one': '"{name}" listesini {count} öğesiyle birlikte silmek istediğinizden emin misiniz?', 'packing.defaultCategory': 'Diğer', + 'packing.dragCategory': 'Kategoriyi yeniden sıralamak için sürükleyin', 'packing.toast.saveError': 'Kaydedilemedi', 'packing.toast.deleteError': 'Silinemedi', 'packing.toast.renameError': 'Yeniden adlandırılamadı', diff --git a/shared/src/i18n/uk/packing.ts b/shared/src/i18n/uk/packing.ts index 6b0ee2346..4c50c7847 100644 --- a/shared/src/i18n/uk/packing.ts +++ b/shared/src/i18n/uk/packing.ts @@ -97,6 +97,7 @@ const packing: TranslationStrings = { 'packing.confirm.deleteCat.few': 'Ви впевнені, що хочете видалити список «{name}» з {count} елементами?', 'packing.confirm.deleteCat.many': 'Ви впевнені, що хочете видалити список «{name}» з {count} елементами?', 'packing.defaultCategory': 'Інше', + 'packing.dragCategory': 'Перетягніть, щоб змінити порядок категорії', 'packing.toast.saveError': 'Помилка збереження', 'packing.toast.deleteError': 'Помилка видалення', 'packing.toast.renameError': 'Помилка перейменування', diff --git a/shared/src/i18n/vi/packing.ts b/shared/src/i18n/vi/packing.ts index 8b521b026..0d41fb413 100644 --- a/shared/src/i18n/vi/packing.ts +++ b/shared/src/i18n/vi/packing.ts @@ -76,6 +76,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': 'Bạn có chắc chắn muốn xóa {count} mục đã chọn không?', 'packing.confirm.deleteCat': 'Bạn có chắc muốn xóa danh sách "{name}" với {count} mục không?', 'packing.defaultCategory': 'Khác', + 'packing.dragCategory': 'Kéo để sắp xếp lại danh mục', 'packing.toast.saveError': 'Không lưu được', 'packing.toast.deleteError': 'Không thể xóa', 'packing.toast.renameError': 'Không thể đổi tên', diff --git a/shared/src/i18n/zh-TW/packing.ts b/shared/src/i18n/zh-TW/packing.ts index b24bf7e6a..9821d03bd 100644 --- a/shared/src/i18n/zh-TW/packing.ts +++ b/shared/src/i18n/zh-TW/packing.ts @@ -75,6 +75,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': '確定移除 {count} 個已勾選的物品?', 'packing.confirm.deleteCat': '確定要刪除清單「{name}」及其 {count} 個物品嗎?', 'packing.defaultCategory': '其他', + 'packing.dragCategory': '拖曳以重新排列類別', 'packing.toast.saveError': '儲存失敗', 'packing.toast.deleteError': '刪除失敗', 'packing.toast.renameError': '重新命名失敗', diff --git a/shared/src/i18n/zh/packing.ts b/shared/src/i18n/zh/packing.ts index ee88f1280..c8c43c7ae 100644 --- a/shared/src/i18n/zh/packing.ts +++ b/shared/src/i18n/zh/packing.ts @@ -75,6 +75,7 @@ const packing: TranslationStrings = { 'packing.confirm.clearChecked': '确定移除 {count} 个已勾选的物品?', 'packing.confirm.deleteCat': '确定要删除清单“{name}”及其 {count} 个物品吗?', 'packing.defaultCategory': '其他', + 'packing.dragCategory': '拖动以重新排列类别', 'packing.toast.saveError': '保存失败', 'packing.toast.deleteError': '删除失败', 'packing.toast.renameError': '重命名失败',