diff --git a/src/main/i18n/locales/cs_CZ/ui.json b/src/main/i18n/locales/cs_CZ/ui.json index f3c5bbf0..e9c10589 100644 --- a/src/main/i18n/locales/cs_CZ/ui.json +++ b/src/main/i18n/locales/cs_CZ/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplikovat", "rename": "Přejmenovat", "restore": "Obnovit", - "setCustomIcon": "Nastavit vlastní ikonu", + "setCustomIcon": "Nastavit ikonu", + "removeCustomIcon": "Odebrat ikonu", "show": "Zobrazit", "hide": "Skrýt", "new": { diff --git a/src/main/i18n/locales/de_DE/ui.json b/src/main/i18n/locales/de_DE/ui.json index 89b209c0..8c878683 100644 --- a/src/main/i18n/locales/de_DE/ui.json +++ b/src/main/i18n/locales/de_DE/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplizieren", "rename": "Umbenennen", "restore": "Wiederherstellen", - "setCustomIcon": "Benutzerdefiniertes Icon festlegen", + "setCustomIcon": "Icon festlegen", + "removeCustomIcon": "Icon entfernen", "show": "Anzeigen", "hide": "Ausblenden", "new": { diff --git a/src/main/i18n/locales/el_GR/ui.json b/src/main/i18n/locales/el_GR/ui.json index 27ee9268..423dc078 100644 --- a/src/main/i18n/locales/el_GR/ui.json +++ b/src/main/i18n/locales/el_GR/ui.json @@ -35,7 +35,8 @@ "duplicate": "Αντίγραφο", "rename": "Μετονομασία", "restore": "Επαναφορά", - "setCustomIcon": "Ορισμός Προσαρμοσμένου Εικονιδίου", + "setCustomIcon": "Ορισμός Εικονιδίου", + "removeCustomIcon": "Αφαίρεση Εικονιδίου", "show": "Εμφάνιση", "hide": "Απόκρυψη", "new": { diff --git a/src/main/i18n/locales/en_US/ui.json b/src/main/i18n/locales/en_US/ui.json index aa57f9f5..7dd9bc56 100644 --- a/src/main/i18n/locales/en_US/ui.json +++ b/src/main/i18n/locales/en_US/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplicate", "rename": "Rename", "restore": "Restore", - "setCustomIcon": "Set Custom Icon", + "setCustomIcon": "Set Icon", + "removeCustomIcon": "Remove Icon", "show": "Show", "hide": "Hide", "new": { diff --git a/src/main/i18n/locales/es_ES/ui.json b/src/main/i18n/locales/es_ES/ui.json index e40215eb..51b6a2b0 100644 --- a/src/main/i18n/locales/es_ES/ui.json +++ b/src/main/i18n/locales/es_ES/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplicar", "rename": "Renombrar", "restore": "Restaurar", - "setCustomIcon": "Establecer Icono Personalizado", + "setCustomIcon": "Establecer Icono", + "removeCustomIcon": "Eliminar Icono", "show": "Mostrar", "hide": "Ocultar", "new": { diff --git a/src/main/i18n/locales/fa_IR/ui.json b/src/main/i18n/locales/fa_IR/ui.json index baaf1258..2531b5b1 100644 --- a/src/main/i18n/locales/fa_IR/ui.json +++ b/src/main/i18n/locales/fa_IR/ui.json @@ -35,7 +35,8 @@ "duplicate": "تکثیر", "rename": "تغییر نام", "restore": "بازیابی", - "setCustomIcon": "تنظیم آیکون سفارشی", + "setCustomIcon": "تنظیم آیکون", + "removeCustomIcon": "حذف آیکون", "show": "نمایش", "hide": "پنهان", "new": { diff --git a/src/main/i18n/locales/fr_FR/ui.json b/src/main/i18n/locales/fr_FR/ui.json index 9bd02bc7..c0103502 100644 --- a/src/main/i18n/locales/fr_FR/ui.json +++ b/src/main/i18n/locales/fr_FR/ui.json @@ -35,7 +35,8 @@ "duplicate": "Dupliquer", "rename": "Renommer", "restore": "Restaurer", - "setCustomIcon": "Définir une icône personnalisée", + "setCustomIcon": "Définir une icône", + "removeCustomIcon": "Supprimer l'icône", "show": "Afficher", "hide": "Masquer", "new": { diff --git a/src/main/i18n/locales/ja_JP/ui.json b/src/main/i18n/locales/ja_JP/ui.json index 191c0c31..1d84cadb 100644 --- a/src/main/i18n/locales/ja_JP/ui.json +++ b/src/main/i18n/locales/ja_JP/ui.json @@ -35,7 +35,8 @@ "duplicate": "複製", "rename": "名前変更", "restore": "復元", - "setCustomIcon": "カスタムアイコンを設定", + "setCustomIcon": "アイコンを設定", + "removeCustomIcon": "アイコンを削除", "show": "表示", "hide": "非表示", "new": { diff --git a/src/main/i18n/locales/pl_PL/ui.json b/src/main/i18n/locales/pl_PL/ui.json index cc860cdc..0fbd3b19 100644 --- a/src/main/i18n/locales/pl_PL/ui.json +++ b/src/main/i18n/locales/pl_PL/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplikuj", "rename": "Zmień nazwę", "restore": "Przywróć", - "setCustomIcon": "Ustaw własną ikonę", + "setCustomIcon": "Ustaw ikonę", + "removeCustomIcon": "Usuń ikonę", "show": "Pokaż", "hide": "Ukryj", "new": { diff --git a/src/main/i18n/locales/pt_BR/ui.json b/src/main/i18n/locales/pt_BR/ui.json index 24d368fb..f5385834 100644 --- a/src/main/i18n/locales/pt_BR/ui.json +++ b/src/main/i18n/locales/pt_BR/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplicar", "rename": "Renomear", "restore": "Restaurar", - "setCustomIcon": "Definir Ícone Personalizado", + "setCustomIcon": "Definir Ícone", + "removeCustomIcon": "Remover Ícone", "show": "Mostrar", "hide": "Ocultar", "new": { diff --git a/src/main/i18n/locales/ro_RO/ui.json b/src/main/i18n/locales/ro_RO/ui.json index bd155ca3..383ebbca 100644 --- a/src/main/i18n/locales/ro_RO/ui.json +++ b/src/main/i18n/locales/ro_RO/ui.json @@ -35,7 +35,8 @@ "duplicate": "Duplică", "rename": "Redenumește", "restore": "Restaurează", - "setCustomIcon": "Setează Iconiță Personalizată", + "setCustomIcon": "Setează Iconiță", + "removeCustomIcon": "Elimină Iconița", "show": "Arată", "hide": "Ascunde", "new": { diff --git a/src/main/i18n/locales/ru_RU/ui.json b/src/main/i18n/locales/ru_RU/ui.json index be5550f8..9aafb800 100644 --- a/src/main/i18n/locales/ru_RU/ui.json +++ b/src/main/i18n/locales/ru_RU/ui.json @@ -35,7 +35,8 @@ "duplicate": "Дублировать", "rename": "Переименовать", "restore": "Восстановить", - "setCustomIcon": "Установить свою иконку", + "setCustomIcon": "Установить иконку", + "removeCustomIcon": "Удалить иконку", "show": "Показать", "hide": "Скрыть", "new": { diff --git a/src/main/i18n/locales/tr_TR/ui.json b/src/main/i18n/locales/tr_TR/ui.json index cee067c1..b70328ab 100644 --- a/src/main/i18n/locales/tr_TR/ui.json +++ b/src/main/i18n/locales/tr_TR/ui.json @@ -35,7 +35,8 @@ "duplicate": "Çoğalt", "rename": "Yeniden Adlandır", "restore": "Geri Yükle", - "setCustomIcon": "Özel İkon Ayarla", + "setCustomIcon": "İkon Ayarla", + "removeCustomIcon": "İkonu Kaldır", "show": "Göster", "hide": "Gizle", "new": { diff --git a/src/main/i18n/locales/uk_UA/ui.json b/src/main/i18n/locales/uk_UA/ui.json index be64f77d..ada7283c 100644 --- a/src/main/i18n/locales/uk_UA/ui.json +++ b/src/main/i18n/locales/uk_UA/ui.json @@ -35,7 +35,8 @@ "duplicate": "Дублювати", "rename": "Перейменувати", "restore": "Відновити", - "setCustomIcon": "Встановити власну іконку", + "setCustomIcon": "Встановити іконку", + "removeCustomIcon": "Видалити іконку", "show": "Показати", "hide": "Приховати", "new": { diff --git a/src/main/i18n/locales/zh_CN/ui.json b/src/main/i18n/locales/zh_CN/ui.json index 28cf0bce..b99429cd 100644 --- a/src/main/i18n/locales/zh_CN/ui.json +++ b/src/main/i18n/locales/zh_CN/ui.json @@ -35,7 +35,8 @@ "duplicate": "复制", "rename": "重命名", "restore": "恢复", - "setCustomIcon": "设置自定义图标", + "setCustomIcon": "设置图标", + "removeCustomIcon": "删除图标", "show": "显示", "hide": "隐藏", "new": { diff --git a/src/main/i18n/locales/zh_HK/ui.json b/src/main/i18n/locales/zh_HK/ui.json index 2bcdf286..92b50ffe 100644 --- a/src/main/i18n/locales/zh_HK/ui.json +++ b/src/main/i18n/locales/zh_HK/ui.json @@ -35,7 +35,8 @@ "duplicate": "複製", "rename": "重命名", "restore": "恢復", - "setCustomIcon": "設置自定義圖標", + "setCustomIcon": "設置圖標", + "removeCustomIcon": "刪除圖標", "show": "顯示", "hide": "隱藏", "new": { diff --git a/src/main/i18n/locales/zh_TW/ui.json b/src/main/i18n/locales/zh_TW/ui.json index f3524003..16c9fad9 100644 --- a/src/main/i18n/locales/zh_TW/ui.json +++ b/src/main/i18n/locales/zh_TW/ui.json @@ -35,7 +35,8 @@ "duplicate": "複製", "rename": "重命名", "restore": "還原", - "setCustomIcon": "設置自定義圖標", + "setCustomIcon": "設置圖標", + "removeCustomIcon": "刪除圖標", "show": "顯示", "hide": "隱藏", "new": { diff --git a/src/renderer/components/sidebar/folders/Tree.vue b/src/renderer/components/sidebar/folders/Tree.vue index c3a185eb..3434ff43 100644 --- a/src/renderer/components/sidebar/folders/Tree.vue +++ b/src/renderer/components/sidebar/folders/Tree.vue @@ -7,6 +7,7 @@ import * as ContextMenu from '@/components/ui/shadcn/context-menu' import { useApp, useDialog, useFolders, useSnippets } from '@/composables' import { i18n } from '@/electron' import { scrollToElement } from '@/utils' +import CustomIcons from './custom-icons/CustomIcons.vue' import { treeKeys } from './keys' import TreeNode from './TreeNode.vue' @@ -37,6 +38,7 @@ const { folders, updateFolder, getFolderByIdFromTree, + getFolders, } = useFolders() const { state } = useApp() const { clearSnippetsState } = useSnippets() @@ -46,7 +48,7 @@ const scrollRef = useTemplateRef('scrollRef') const hoveredNodeId = ref('') const isHoveredByIdDisabled = ref(false) -const contextNodeId = ref(null) +const contextNode = ref(null) function clickNode(id: number) { return emit('clickNode', id) @@ -67,7 +69,7 @@ function toggleNode(node: Node) { * Так же такое решение избавит от n кол-ва ContextMenu на каждый узел. */ function contextMenu(node: Node, event: MouseEvent) { - contextNodeId.value = node.id + contextNode.value = node contextMenuTriggerRef.value?.dispatchEvent( new MouseEvent('contextmenu', { bubbles: false, @@ -78,11 +80,14 @@ function contextMenu(node: Node, event: MouseEvent) { } async function onDeleteFolder() { + if (!contextNode.value) + return + const { confirm } = useDialog() const folderName = getFolderByIdFromTree( folders.value, - contextNodeId.value, + contextNode.value.id, )?.name const isConfirmed = await confirm({ @@ -90,10 +95,10 @@ async function onDeleteFolder() { description: i18n.t('messages:warning:allSnippetsMoveToTrash'), }) - if (isConfirmed && contextNodeId.value) { - await deleteFolder(contextNodeId.value) + if (isConfirmed) { + await deleteFolder(contextNode.value.id) - if (contextNodeId.value === state.folderId) { + if (contextNode.value.id === state.folderId) { state.folderId = undefined clearSnippetsState() @@ -117,20 +122,48 @@ function onRenameFolder() { // FIXME: Костыль для того чтобы input в TreeNode фокусировался, // разобраться почему не работает nextTick setTimeout(() => { - renameFolderId.value = contextNodeId.value + if (!contextNode.value) + return + + renameFolderId.value = contextNode.value.id }, 100) } function onSelectLanguage(language: string) { - if (!contextNodeId.value) { + if (!contextNode.value) { return } - updateFolder(contextNodeId.value, { + updateFolder(contextNode.value.id, { defaultLanguage: language, }) } +function onSetCustomIcon() { + if (!contextNode.value) + return + + const { showDialog } = useDialog() + + showDialog({ + title: i18n.t('action.setCustomIcon'), + content: h(CustomIcons, { + nodeId: contextNode.value.id, + }), + }) +} + +async function onRemoveCustomIcon() { + if (!contextNode.value) + return + + updateFolder(contextNode.value.id, { + icon: null, + }) + + await getFolders() +} + provide(treeKeys, { clickNode, contextMenu, @@ -188,6 +221,16 @@ provide(treeKeys, { {{ i18n.t("action.delete.common") }} + + {{ i18n.t("action.setCustomIcon") }} + + + {{ i18n.t("action.removeCustomIcon") }} + + {{ i18n.t("action.defaultLanguage") }} diff --git a/src/renderer/components/sidebar/folders/custom-icons/CustomIcons.vue b/src/renderer/components/sidebar/folders/custom-icons/CustomIcons.vue new file mode 100644 index 00000000..59e0709e --- /dev/null +++ b/src/renderer/components/sidebar/folders/custom-icons/CustomIcons.vue @@ -0,0 +1,125 @@ + + + diff --git a/src/renderer/components/sidebar/folders/custom-icons/icons.ts b/src/renderer/components/sidebar/folders/custom-icons/icons.ts new file mode 100644 index 00000000..097b25f2 --- /dev/null +++ b/src/renderer/components/sidebar/folders/custom-icons/icons.ts @@ -0,0 +1,20 @@ +const files = import.meta.glob('@/assets/svg/icons/**.svg', { + as: 'raw', + eager: true, +}) +const re = /\/([^/]+)\.svg$/ + +const iconsSet: Record = {} + +const icons = Object.entries(files).map(([k, v]) => { + const name = k.match(re)?.[1] + if (name) { + iconsSet[name] = v as unknown as string + } + return { + name, + source: v, + } +}) + +export { icons, iconsSet } diff --git a/src/renderer/composables/useDialog.ts b/src/renderer/composables/useDialog.ts index c28b561a..287cbb75 100644 --- a/src/renderer/composables/useDialog.ts +++ b/src/renderer/composables/useDialog.ts @@ -16,7 +16,7 @@ export interface DialogOptions { description?: string confirmText?: string cancelText?: string - content?: any + content?: string | Component } export function useDialog() { @@ -119,34 +119,38 @@ export function useDialog() { ], }, ), - content + content && typeof content === 'string' ? h('div', { class: '' }, { default: () => content }) + : content && typeof content === 'object' + ? h(content) + : null, + confirmText && cancelText + ? h( + Dialog.DialogFooter, + {}, + { + default: () => [ + h( + Button, + { + size: 'md', + onClick: onCancel, + }, + { default: () => cancelText }, + ), + h( + Button, + { + variant: 'primary', + size: 'md', + onClick: onConfirm, + }, + { default: () => confirmText }, + ), + ], + }, + ) : null, - h( - Dialog.DialogFooter, - {}, - { - default: () => [ - h( - Button, - { - size: 'md', - onClick: onCancel, - }, - { default: () => cancelText }, - ), - h( - Button, - { - variant: 'primary', - size: 'md', - onClick: onConfirm, - }, - { default: () => confirmText }, - ), - ], - }, - ), ], }, ),