diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/App.vue | 7 | ||||
-rw-r--r-- | src/components/CollapsibleToolMenu.vue | 4 | ||||
-rw-r--r-- | src/layouts/base.layout.vue | 18 | ||||
-rw-r--r-- | src/modules/command-palette/command-palette.vue | 2 | ||||
-rw-r--r-- | src/modules/i18n/components/locale-selector.vue | 28 | ||||
-rw-r--r-- | src/pages/Home.page.vue | 3 | ||||
-rw-r--r-- | src/tools/tools.store.ts | 65 | ||||
-rw-r--r-- | src/ui/c-select/c-select.demo.vue | 15 | ||||
-rw-r--r-- | src/ui/c-select/c-select.vue | 16 |
9 files changed, 116 insertions, 42 deletions
diff --git a/src/App.vue b/src/App.vue index fec26bf..8e65335 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,6 +11,13 @@ const styleStore = useStyleStore(); const theme = computed(() => (styleStore.isDarkTheme ? darkTheme : null)); const themeOverrides = computed(() => (styleStore.isDarkTheme ? darkThemeOverrides : lightThemeOverrides)); + +const { locale } = useI18n(); + +syncRef( + locale, + useStorage('locale', locale), +); </script> <template> diff --git a/src/components/CollapsibleToolMenu.vue b/src/components/CollapsibleToolMenu.vue index 3a025ba..9a73ddd 100644 --- a/src/components/CollapsibleToolMenu.vue +++ b/src/components/CollapsibleToolMenu.vue @@ -36,7 +36,7 @@ const menuOptions = computed(() => tools: components.map(tool => ({ label: makeLabel(tool), icon: makeIcon(tool), - key: tool.name, + key: tool.path, })), })), ); @@ -62,7 +62,7 @@ const themeVars = useThemeVars(); <n-menu class="menu" - :value="route.name as string" + :value="route.path" :collapsed-width="64" :collapsed-icon-size="22" :options="tools" diff --git a/src/layouts/base.layout.vue b/src/layouts/base.layout.vue index 2bbb67f..950b01d 100644 --- a/src/layouts/base.layout.vue +++ b/src/layouts/base.layout.vue @@ -4,10 +4,10 @@ import { NIcon, useThemeVars } from 'naive-ui'; import { RouterLink } from 'vue-router'; import { Heart, Home2, Menu2 } from '@vicons/tabler'; +import { storeToRefs } from 'pinia'; import HeroGradient from '../assets/hero-gradient.svg?component'; import MenuLayout from '../components/MenuLayout.vue'; import NavbarButtons from '../components/NavbarButtons.vue'; -import { toolsByCategory } from '@/tools'; import { useStyleStore } from '@/stores/style.store'; import { config } from '@/config'; import type { ToolCategory } from '@/tools/tools.types'; @@ -21,12 +21,14 @@ const version = config.app.version; const commitSha = config.app.lastCommitSha.slice(0, 7); const { tracker } = useTracker(); +const { t } = useI18n(); const toolStore = useToolStore(); +const { favoriteTools, toolsByCategory } = storeToRefs(toolStore); const tools = computed<ToolCategory[]>(() => [ - ...(toolStore.favoriteTools.length > 0 ? [{ name: 'Your favorite tools', components: toolStore.favoriteTools }] : []), - ...toolsByCategory, + ...(favoriteTools.value.length > 0 ? [{ name: t('tools.categories.favorite-tools'), components: favoriteTools.value }] : []), + ...toolsByCategory.value, ]); </script> @@ -47,8 +49,12 @@ const tools = computed<ToolCategory[]>(() => [ </RouterLink> <div class="sider-content"> - <div v-if="styleStore.isSmallScreen" flex justify-center> - <NavbarButtons /> + <div v-if="styleStore.isSmallScreen" flex flex-col items-center> + <locale-selector w="90%" /> + + <div flex justify-center> + <NavbarButtons /> + </div> </div> <CollapsibleToolMenu :tools-by-category="tools" /> @@ -108,6 +114,8 @@ const tools = computed<ToolCategory[]>(() => [ <command-palette /> + <locale-selector v-if="!styleStore.isSmallScreen" /> + <div> <NavbarButtons v-if="!styleStore.isSmallScreen" /> </div> diff --git a/src/modules/command-palette/command-palette.vue b/src/modules/command-palette/command-palette.vue index 7531aac..bceef5c 100644 --- a/src/modules/command-palette/command-palette.vue +++ b/src/modules/command-palette/command-palette.vue @@ -116,7 +116,7 @@ function activateOption(option: PaletteOption) { <span flex items-center gap-3 op-40> <icon-mdi-search /> - Search... + {{ $t('search.label') }} <span hidden flex-1 border border-current border-op-40 rounded border-solid px-5px py-3px sm:inline> {{ isMac ? 'Cmd' : 'Ctrl' }} + K diff --git a/src/modules/i18n/components/locale-selector.vue b/src/modules/i18n/components/locale-selector.vue new file mode 100644 index 0000000..29dc0e5 --- /dev/null +++ b/src/modules/i18n/components/locale-selector.vue @@ -0,0 +1,28 @@ +<script setup lang="ts"> +const { availableLocales, locale } = useI18n(); + +const localesLong: Record<string, string> = { + en: 'English', + es: 'Español', + fr: 'Français', + pt: 'Português', + ru: 'Русский', + zh: '中文', +}; + +const localeOptions = computed(() => + availableLocales.map(locale => ({ + label: localesLong[locale] ?? locale, + value: locale, + })), +); +</script> + +<template> + <c-select + v-model:value="locale" + :options="localeOptions" + placeholder="Select a language" + w-100px + /> +</template> diff --git a/src/pages/Home.page.vue b/src/pages/Home.page.vue index 7f34081..859418e 100644 --- a/src/pages/Home.page.vue +++ b/src/pages/Home.page.vue @@ -31,7 +31,8 @@ const { t } = useI18n(); rel="noopener" target="_blank" :aria-label="$t('home.follow.twitterAccount')" - >Twitter</a>{{ $t('home.follow.thankYou') }} + >Twitter</a>. + {{ $t('home.follow.thankYou') }} <n-icon :component="Heart" /> </ColoredCard> </n-gi> diff --git a/src/tools/tools.store.ts b/src/tools/tools.store.ts index 769b4d8..d952b7c 100644 --- a/src/tools/tools.store.ts +++ b/src/tools/tools.store.ts @@ -1,44 +1,57 @@ import { type MaybeRef, get, useStorage } from '@vueuse/core'; import { defineStore } from 'pinia'; import type { Ref } from 'vue'; -import type { Tool, ToolWithCategory } from './tools.types'; +import _ from 'lodash'; +import type { Tool, ToolCategory, ToolWithCategory } from './tools.types'; import { toolsWithCategory } from './index'; -export const useToolStore = defineStore('tools', { - state: () => ({ - favoriteToolsName: useStorage('favoriteToolsName', []) as Ref<string[]>, - }), - getters: { - favoriteTools(state) { - return state.favoriteToolsName - .map(favoriteName => toolsWithCategory.find(({ name }) => name === favoriteName)) - .filter(Boolean) as ToolWithCategory[]; // cast because .filter(Boolean) does not remove undefined from type - }, +export const useToolStore = defineStore('tools', () => { + const favoriteToolsName = useStorage('favoriteToolsName', []) as Ref<string[]>; + const { t } = useI18n(); - notFavoriteTools(state): ToolWithCategory[] { - return toolsWithCategory.filter(tool => !state.favoriteToolsName.includes(tool.name)); - }, + const tools = computed<ToolWithCategory[]>(() => toolsWithCategory.map((tool) => { + const toolI18nKey = tool.path.replace(/\//g, ''); - tools(): ToolWithCategory[] { - return toolsWithCategory; - }, + return ({ + ...tool, + name: t(`tools.${toolI18nKey}.title`, tool.name), + description: t(`tools.${toolI18nKey}.description`, tool.description), + category: t(`tools.categories.${tool.category.toLowerCase()}`, tool.category), + }); + })); - newTools(): ToolWithCategory[] { - return this.tools.filter(({ isNew }) => isNew); - }, - }, + const toolsByCategory = computed<ToolCategory[]>(() => { + return _.chain(tools.value) + .groupBy('category') + .map((components, name) => ({ + name, + components, + })) + .value(); + }); + + const favoriteTools = computed(() => { + return favoriteToolsName.value + .map(favoriteName => tools.value.find(({ name }) => name === favoriteName)) + .filter(Boolean) as ToolWithCategory[]; // cast because .filter(Boolean) does not remove undefined from type + }); + + return { + tools, + favoriteTools, + toolsByCategory, + newTools: computed(() => tools.value.filter(({ isNew }) => isNew)), - actions: { addToolToFavorites({ tool }: { tool: MaybeRef<Tool> }) { - this.favoriteToolsName.push(get(tool).name); + favoriteToolsName.value.push(get(tool).name); }, removeToolFromFavorites({ tool }: { tool: MaybeRef<Tool> }) { - this.favoriteToolsName = this.favoriteToolsName.filter(name => get(tool).name !== name); + favoriteToolsName.value = favoriteToolsName.value.filter(name => get(tool).name !== name); }, isToolFavorite({ tool }: { tool: MaybeRef<Tool> }) { - return this.favoriteToolsName.includes(get(tool).name); + return favoriteToolsName.value.includes(get(tool).name); }, - }, + }; }); diff --git a/src/ui/c-select/c-select.demo.vue b/src/ui/c-select/c-select.demo.vue index ae553bb..f656c01 100644 --- a/src/ui/c-select/c-select.demo.vue +++ b/src/ui/c-select/c-select.demo.vue @@ -33,4 +33,19 @@ const value = ref(''); <c-select label="Label" label-position="left" label-align="left" mb-2 label-width="200px" /> <c-select label="Label" label-position="left" label-align="center" mb-2 label-width="200px" /> <c-select label="Label" label-position="left" label-align="right" mb-2 label-width="200px" /> + + <h2>Custom displayed value</h2> + <c-select v-model:value="value" :options="optionsA" mb-2> + <template #displayed-value> + <span class="font-bold lh-normal">Hello</span> + </template> + </c-select> + + <c-select v-model:value="value" :options="optionsA"> + <template #displayed-value> + <span lh-normal> + <icon-mdi-translate /> + </span> + </template> + </c-select> </template> diff --git a/src/ui/c-select/c-select.vue b/src/ui/c-select/c-select.vue index fb34038..7b3607c 100644 --- a/src/ui/c-select/c-select.vue +++ b/src/ui/c-select/c-select.vue @@ -150,13 +150,15 @@ function onSearchInput() { @keydown="handleKeydown" > <div flex-1 truncate> - <input v-if="searchable && isOpen" ref="searchInputRef" v-model="searchQuery" type="text" placeholder="Search..." class="search-input" w-full lh-normal color-current @input="onSearchInput"> - <span v-else-if="selectedOption" lh-normal> - {{ selectedOption.label }} - </span> - <span v-else class="placeholder" lh-normal> - {{ placeholder ?? 'Select an option' }} - </span> + <slot name="displayed-value"> + <input v-if="searchable && isOpen" ref="searchInputRef" v-model="searchQuery" type="text" placeholder="Search..." class="search-input" w-full lh-normal color-current @input="onSearchInput"> + <span v-else-if="selectedOption" lh-normal> + {{ selectedOption.label }} + </span> + <span v-else class="placeholder" lh-normal> + {{ placeholder ?? 'Select an option' }} + </span> + </slot> </div> <icon-mdi-chevron-down class="chevron" /> |