mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 11:37:58 +00:00
feat: Update button component (#10362)
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import { OnClickOutside } from '@vueuse/components';
|
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { dynamicTime } from 'shared/helpers/timeHelper';
|
import { dynamicTime } from 'shared/helpers/timeHelper';
|
||||||
import {
|
import {
|
||||||
@@ -9,11 +8,11 @@ import {
|
|||||||
ARTICLE_STATUSES,
|
ARTICLE_STATUSES,
|
||||||
} from 'dashboard/helper/portalHelper';
|
} from 'dashboard/helper/portalHelper';
|
||||||
|
|
||||||
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
import CardLayout from 'dashboard/components-next/CardLayout.vue';
|
import CardLayout from 'dashboard/components-next/CardLayout.vue';
|
||||||
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
import DropdownMenu from 'dashboard/components-next/dropdown-menu/DropdownMenu.vue';
|
||||||
import Button from 'dashboard/components-next/button/Button.vue';
|
import Button from 'dashboard/components-next/button/Button.vue';
|
||||||
import Thumbnail from 'dashboard/components-next/thumbnail/Thumbnail.vue';
|
import Thumbnail from 'dashboard/components-next/thumbnail/Thumbnail.vue';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
@@ -72,11 +71,11 @@ const articleMenuItems = computed(() => {
|
|||||||
const statusTextColor = computed(() => {
|
const statusTextColor = computed(() => {
|
||||||
switch (props.status) {
|
switch (props.status) {
|
||||||
case 'archived':
|
case 'archived':
|
||||||
return '!text-n-slate-12';
|
return 'text-n-slate-12';
|
||||||
case 'draft':
|
case 'draft':
|
||||||
return '!text-n-amber-11';
|
return 'text-n-amber-11';
|
||||||
default:
|
default:
|
||||||
return '!text-n-teal-11';
|
return 'text-n-teal-11';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -127,19 +126,28 @@ const handleClick = id => {
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex justify-between gap-1">
|
<div class="flex justify-between gap-1">
|
||||||
<span
|
<span
|
||||||
class="text-base cursor-pointer hover:underline text-n-slate-12 line-clamp-1"
|
class="text-base cursor-pointer hover:underline underline-offset-2 hover:text-n-blue-text text-n-slate-12 line-clamp-1"
|
||||||
@click="handleClick(id)"
|
@click="handleClick(id)"
|
||||||
>
|
>
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</span>
|
</span>
|
||||||
<div class="relative group" @click.stop>
|
<div class="flex items-center gap-2">
|
||||||
<OnClickOutside @trigger="isOpen = false">
|
<span
|
||||||
|
class="text-xs font-medium inline-flex items-center h-6 px-2 py-0.5 rounded-md bg-n-alpha-2"
|
||||||
|
:class="statusTextColor"
|
||||||
|
>
|
||||||
|
{{ statusText }}
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
v-on-clickaway="() => (isOpen = false)"
|
||||||
|
class="relative flex items-center group"
|
||||||
|
@click.stop
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
icon="i-lucide-ellipsis-vertical"
|
||||||
size="sm"
|
color="slate"
|
||||||
class="text-xs font-medium bg-n-alpha-2 hover:bg-n-alpha-1 !h-6 rounded-md border-0 !px-2 !py-0.5"
|
size="xs"
|
||||||
:label="statusText"
|
class="group-hover:bg-n-solid-2 !p-0.5 rounded-md"
|
||||||
:class="statusTextColor"
|
|
||||||
@click="isOpen = !isOpen"
|
@click="isOpen = !isOpen"
|
||||||
/>
|
/>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
@@ -148,7 +156,7 @@ const handleClick = id => {
|
|||||||
class="mt-1 ltr:right-0 rtl:left-0 xl:ltr:left-0 xl:rtl:right-0 top-full"
|
class="mt-1 ltr:right-0 rtl:left-0 xl:ltr:left-0 xl:rtl:right-0 top-full"
|
||||||
@action="handleArticleAction($event)"
|
@action="handleArticleAction($event)"
|
||||||
/>
|
/>
|
||||||
</OnClickOutside>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -171,7 +179,7 @@ const handleClick = id => {
|
|||||||
<div
|
<div
|
||||||
class="inline-flex items-center gap-1 text-n-slate-11 whitespace-nowrap"
|
class="inline-flex items-center gap-1 text-n-slate-11 whitespace-nowrap"
|
||||||
>
|
>
|
||||||
<FluentIcon icon="eye-show" size="18" />
|
<Icon icon="i-lucide-eye" class="size-4" />
|
||||||
<span class="text-sm">
|
<span class="text-sm">
|
||||||
{{
|
{{
|
||||||
t('HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.VIEWS', {
|
t('HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.VIEWS', {
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const handleAction = ({ action, value }) => {
|
|||||||
<div class="flex justify-between w-full gap-1">
|
<div class="flex justify-between w-full gap-1">
|
||||||
<div class="flex items-center justify-start gap-2">
|
<div class="flex items-center justify-start gap-2">
|
||||||
<span
|
<span
|
||||||
class="text-base cursor-pointer hover:underline text-slate-900 dark:text-slate-50 line-clamp-1"
|
class="text-base cursor-pointer hover:underline underline-offset-2 hover:text-n-blue-text text-n-slate-12 line-clamp-1"
|
||||||
@click="handleClick(slug)"
|
@click="handleClick(slug)"
|
||||||
>
|
>
|
||||||
{{ categoryTitleWithIcon }}
|
{{ categoryTitleWithIcon }}
|
||||||
@@ -102,10 +102,10 @@ const handleAction = ({ action, value }) => {
|
|||||||
<div class="relative group" @click.stop>
|
<div class="relative group" @click.stop>
|
||||||
<OnClickOutside @trigger="isOpen = false">
|
<OnClickOutside @trigger="isOpen = false">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
icon="i-lucide-ellipsis-vertical"
|
||||||
size="sm"
|
color="slate"
|
||||||
icon="more-vertical"
|
size="xs"
|
||||||
class="w-8 z-60 group-hover:bg-slate-100 dark:group-hover:bg-slate-800"
|
class="rounded-md group-hover:bg-n-solid-2"
|
||||||
@click="isOpen = !isOpen"
|
@click="isOpen = !isOpen"
|
||||||
/>
|
/>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
|
|||||||
@@ -49,12 +49,7 @@ const onClick = () => {
|
|||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<div v-if="showButton">
|
<div v-if="showButton">
|
||||||
<Button
|
<Button :label="buttonLabel" icon="i-lucide-plus" @click="onClick" />
|
||||||
variant="default"
|
|
||||||
:label="buttonLabel"
|
|
||||||
icon="add"
|
|
||||||
@click="onClick"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</EmptyStateLayout>
|
</EmptyStateLayout>
|
||||||
|
|||||||
@@ -59,9 +59,8 @@ const onPortalCreate = ({ slug: portalSlug, locale }) => {
|
|||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
|
||||||
:label="$t('HELP_CENTER.NEW_PAGE.CREATE_PORTAL_BUTTON')"
|
:label="$t('HELP_CENTER.NEW_PAGE.CREATE_PORTAL_BUTTON')"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
@click="openDialog"
|
@click="openDialog"
|
||||||
/>
|
/>
|
||||||
<CreatePortalDialog
|
<CreatePortalDialog
|
||||||
|
|||||||
@@ -74,10 +74,10 @@ const togglePortalSwitcher = () => {
|
|||||||
<div v-if="activePortalName" class="relative group">
|
<div v-if="activePortalName" class="relative group">
|
||||||
<OnClickOutside @trigger="showPortalSwitcher = false">
|
<OnClickOutside @trigger="showPortalSwitcher = false">
|
||||||
<Button
|
<Button
|
||||||
icon="chevron-lucide-down"
|
icon="i-lucide-chevron-down"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
icon-lib="lucide"
|
size="xs"
|
||||||
class="!w-6 !h-6 group-hover:bg-n-solid-2 !p-0.5 rounded-md"
|
class="rounded-md group-hover:bg-n-slate-3 hover:bg-n-slate-3"
|
||||||
@click="togglePortalSwitcher"
|
@click="togglePortalSwitcher"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const handleAction = ({ action, value }) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<CardLayout class="ltr:pr-2 rtl:pl-2">
|
<CardLayout>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="flex justify-between gap-2">
|
<div class="flex justify-between gap-2">
|
||||||
<div class="flex items-center justify-start gap-2">
|
<div class="flex items-center justify-start gap-2">
|
||||||
@@ -63,12 +63,12 @@ const handleAction = ({ action, value }) => {
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="isDefault"
|
v-if="isDefault"
|
||||||
class="bg-slate-100 dark:bg-slate-800 h-6 inline-flex items-center justify-center rounded-md text-xs border-px border-transparent text-woot-500 dark:text-woot-400 px-2 py-0.5"
|
class="bg-n-alpha-2 h-6 inline-flex items-center justify-center rounded-md text-xs border-px border-transparent text-n-blue-text px-2 py-0.5"
|
||||||
>
|
>
|
||||||
{{ $t('HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DEFAULT') }}
|
{{ $t('HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DEFAULT') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-4">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span
|
<span
|
||||||
class="text-sm text-slate-500 dark:text-slate-400 whitespace-nowrap"
|
class="text-sm text-slate-500 dark:text-slate-400 whitespace-nowrap"
|
||||||
@@ -95,10 +95,10 @@ const handleAction = ({ action, value }) => {
|
|||||||
<div class="relative group">
|
<div class="relative group">
|
||||||
<OnClickOutside @trigger="showDropdownMenu = false">
|
<OnClickOutside @trigger="showDropdownMenu = false">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
icon="i-lucide-ellipsis-vertical"
|
||||||
size="sm"
|
color="slate"
|
||||||
icon="more-vertical"
|
size="xs"
|
||||||
class="w-8 group-hover:bg-slate-100 dark:group-hover:bg-slate-800"
|
class="rounded-md group-hover:bg-n-solid-2"
|
||||||
@click="showDropdownMenu = !showDropdownMenu"
|
@click="showDropdownMenu = !showDropdownMenu"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ const agentList = computed(() => {
|
|||||||
label: name,
|
label: name,
|
||||||
value: id,
|
value: id,
|
||||||
thumbnail: { name, src: thumbnail },
|
thumbnail: { name, src: thumbnail },
|
||||||
isSelected:
|
isSelected: props.article?.author?.id
|
||||||
id === props.article?.author?.id ||
|
? id === props.article.author.id
|
||||||
id === (selectedAuthorId.value || currentUserId.value),
|
: id === (selectedAuthorId.value || currentUserId.value),
|
||||||
action: 'assignAuthor',
|
action: 'assignAuthor',
|
||||||
}))
|
}))
|
||||||
// Sort the list by isSelected first, then by name(label)
|
// Sort the list by isSelected first, then by name(label)
|
||||||
@@ -181,20 +181,23 @@ onMounted(() => {
|
|||||||
<div class="relative flex items-center gap-2">
|
<div class="relative flex items-center gap-2">
|
||||||
<OnClickOutside @trigger="openAgentsList = false">
|
<OnClickOutside @trigger="openAgentsList = false">
|
||||||
<Button
|
<Button
|
||||||
:label="authorName"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="!px-0 font-normal"
|
class="!px-0 font-normal hover:!bg-transparent"
|
||||||
text-variant="info"
|
text-variant="info"
|
||||||
@click="openAgentsList = !openAgentsList"
|
@click="openAgentsList = !openAgentsList"
|
||||||
>
|
>
|
||||||
<template #leftPrefix>
|
<Thumbnail
|
||||||
<Thumbnail
|
:author="author"
|
||||||
:author="author"
|
:name="authorName"
|
||||||
:name="authorName"
|
:size="20"
|
||||||
:size="20"
|
:src="authorThumbnailSrc"
|
||||||
:src="authorThumbnailSrc"
|
/>
|
||||||
/>
|
<span
|
||||||
</template>
|
v-if="author"
|
||||||
|
class="text-sm text-n-slate-12 hover:text-n-slate-11"
|
||||||
|
>
|
||||||
|
{{ author.available_name }}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
v-if="openAgentsList && hasAgentList"
|
v-if="openAgentsList && hasAgentList"
|
||||||
@@ -212,13 +215,20 @@ onMounted(() => {
|
|||||||
selectedCategory?.name ||
|
selectedCategory?.name ||
|
||||||
t('HELP_CENTER.EDIT_ARTICLE_PAGE.EDIT_ARTICLE.UNCATEGORIZED')
|
t('HELP_CENTER.EDIT_ARTICLE_PAGE.EDIT_ARTICLE.UNCATEGORIZED')
|
||||||
"
|
"
|
||||||
:emoji="selectedCategory?.icon || ''"
|
:icon="!selectedCategory?.icon ? 'i-lucide-shapes' : ''"
|
||||||
:icon="!selectedCategory?.icon ? 'play-shape' : ''"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="!px-2 font-normal"
|
class="!px-2 font-normal hover:!bg-transparent"
|
||||||
text-variant="info"
|
|
||||||
@click="openCategoryList = !openCategoryList"
|
@click="openCategoryList = !openCategoryList"
|
||||||
/>
|
>
|
||||||
|
<span
|
||||||
|
v-if="selectedCategory"
|
||||||
|
class="text-sm text-n-slate-12 hover:text-n-slate-11"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
`${selectedCategory.icon || ''} ${selectedCategory.name || t('HELP_CENTER.EDIT_ARTICLE_PAGE.EDIT_ARTICLE.UNCATEGORIZED')}`
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
v-if="openCategoryList && hasCategoryMenuItems"
|
v-if="openCategoryList && hasCategoryMenuItems"
|
||||||
:menu-items="categoryList"
|
:menu-items="categoryList"
|
||||||
@@ -235,11 +245,10 @@ onMounted(() => {
|
|||||||
:label="
|
:label="
|
||||||
t('HELP_CENTER.EDIT_ARTICLE_PAGE.EDIT_ARTICLE.MORE_PROPERTIES')
|
t('HELP_CENTER.EDIT_ARTICLE_PAGE.EDIT_ARTICLE.MORE_PROPERTIES')
|
||||||
"
|
"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
:disabled="isNewArticle"
|
:disabled="isNewArticle"
|
||||||
text-variant="info"
|
class="!px-2 font-normal hover:!bg-transparent hover:!text-n-slate-11"
|
||||||
class="!px-2 font-normal"
|
|
||||||
@click="openProperties = !openProperties"
|
@click="openProperties = !openProperties"
|
||||||
/>
|
/>
|
||||||
<ArticleEditorProperties
|
<ArticleEditorProperties
|
||||||
|
|||||||
@@ -118,11 +118,11 @@ const updateArticleStatus = async ({ value }) => {
|
|||||||
<div class="flex items-center justify-between h-20">
|
<div class="flex items-center justify-between h-20">
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.EDIT_ARTICLE_PAGE.HEADER.BACK_TO_ARTICLES')"
|
:label="t('HELP_CENTER.EDIT_ARTICLE_PAGE.HEADER.BACK_TO_ARTICLES')"
|
||||||
icon="chevron-lucide-left"
|
icon="i-lucide-chevron-left"
|
||||||
icon-lib="lucide"
|
|
||||||
variant="link"
|
variant="link"
|
||||||
text-variant="info"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="ltr:pl-3 rtl:pr-3"
|
||||||
@click="onClickGoBack"
|
@click="onClickGoBack"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
@@ -135,7 +135,7 @@ const updateArticleStatus = async ({ value }) => {
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.EDIT_ARTICLE_PAGE.HEADER.PREVIEW')"
|
:label="t('HELP_CENTER.EDIT_ARTICLE_PAGE.HEADER.PREVIEW')"
|
||||||
variant="secondary"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
:disabled="!articleId"
|
:disabled="!articleId"
|
||||||
@click="previewArticle"
|
@click="previewArticle"
|
||||||
@@ -156,8 +156,7 @@ const updateArticleStatus = async ({ value }) => {
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<OnClickOutside @trigger="showArticleActionMenu = false">
|
<OnClickOutside @trigger="showArticleActionMenu = false">
|
||||||
<Button
|
<Button
|
||||||
icon="chevron-lucide-down"
|
icon="i-lucide-chevron-down"
|
||||||
icon-lib="lucide"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
:disabled="!articleId"
|
:disabled="!articleId"
|
||||||
class="ltr:rounded-l-none rtl:rounded-r-none"
|
class="ltr:rounded-l-none rtl:rounded-r-none"
|
||||||
|
|||||||
@@ -63,10 +63,10 @@ onMounted(() => {
|
|||||||
}}
|
}}
|
||||||
</h3>
|
</h3>
|
||||||
<Button
|
<Button
|
||||||
icon="dismiss"
|
icon="i-lucide-x"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
class="w-8 hover:text-n-slate-11"
|
class="hover:text-n-slate-11"
|
||||||
@click="emit('close')"
|
@click="emit('close')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -89,7 +89,7 @@ onMounted(() => {
|
|||||||
'HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_DESCRIPTION_PLACEHOLDER'
|
'HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_DESCRIPTION_PLACEHOLDER'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
class="w-[224px]"
|
class="w-[220px]"
|
||||||
custom-text-area-wrapper-class="!p-0 !border-0 !rounded-none !bg-transparent transition-none"
|
custom-text-area-wrapper-class="!p-0 !border-0 !rounded-none !bg-transparent transition-none"
|
||||||
custom-text-area-class="max-h-[150px]"
|
custom-text-area-class="max-h-[150px]"
|
||||||
auto-height
|
auto-height
|
||||||
|
|||||||
@@ -148,10 +148,9 @@ const handleTabChange = value => {
|
|||||||
<Button
|
<Button
|
||||||
:label="activeLocaleName"
|
:label="activeLocaleName"
|
||||||
size="sm"
|
size="sm"
|
||||||
icon-position="right"
|
icon="i-lucide-chevron-down"
|
||||||
icon="chevron-lucide-down"
|
color="slate"
|
||||||
icon-lib="lucide"
|
trailing-icon
|
||||||
variant="secondary"
|
|
||||||
@click="isLocaleMenuOpen = !isLocaleMenuOpen"
|
@click="isLocaleMenuOpen = !isLocaleMenuOpen"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -167,11 +166,10 @@ const handleTabChange = value => {
|
|||||||
<OnClickOutside @trigger="isCategoryMenuOpen = false">
|
<OnClickOutside @trigger="isCategoryMenuOpen = false">
|
||||||
<Button
|
<Button
|
||||||
:label="activeCategoryName"
|
:label="activeCategoryName"
|
||||||
|
icon="i-lucide-chevron-down"
|
||||||
size="sm"
|
size="sm"
|
||||||
icon-position="right"
|
color="slate"
|
||||||
icon="chevron-lucide-down"
|
trailing-icon
|
||||||
icon-lib="lucide"
|
|
||||||
variant="secondary"
|
|
||||||
class="max-w-48"
|
class="max-w-48"
|
||||||
@click="isCategoryMenuOpen = !isCategoryMenuOpen"
|
@click="isCategoryMenuOpen = !isCategoryMenuOpen"
|
||||||
/>
|
/>
|
||||||
@@ -187,7 +185,7 @@ const handleTabChange = value => {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.ARTICLES_PAGE.ARTICLES_HEADER.NEW_ARTICLE')"
|
:label="t('HELP_CENTER.ARTICLES_PAGE.ARTICLES_HEADER.NEW_ARTICLE')"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="handleNewArticle"
|
@click="handleNewArticle"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -198,10 +198,10 @@ defineExpose({ state, isSubmitDisabled });
|
|||||||
<OnClickOutside @trigger="isEmojiPickerOpen = false">
|
<OnClickOutside @trigger="isEmojiPickerOpen = false">
|
||||||
<Button
|
<Button
|
||||||
:label="state.icon"
|
:label="state.icon"
|
||||||
variant="secondary"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
:icon="!state.icon ? 'emoji-add' : ''"
|
:icon="!state.icon ? 'i-lucide-smile-plus' : ''"
|
||||||
class="!h-[38px] !w-[38px] absolute top-[31px] !rounded-[7px] border-0 ltr:left-px rtl:right-px ltr:!rounded-r-none rtl:!rounded-l-none"
|
class="!h-[38px] !w-[38px] absolute top-[31px] !outline-none !rounded-[7px] border-0 ltr:left-px rtl:right-px ltr:!rounded-r-none rtl:!rounded-l-none"
|
||||||
@click="isEmojiPickerOpen = !isEmojiPickerOpen"
|
@click="isEmojiPickerOpen = !isEmojiPickerOpen"
|
||||||
/>
|
/>
|
||||||
<EmojiInput
|
<EmojiInput
|
||||||
@@ -243,10 +243,10 @@ defineExpose({ state, isSubmitDisabled });
|
|||||||
class="flex items-center justify-between w-full gap-3"
|
class="flex items-center justify-between w-full gap-3"
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="faded"
|
||||||
|
color="slate"
|
||||||
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_DIALOG.BUTTONS.CANCEL')"
|
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_DIALOG.BUTTONS.CANCEL')"
|
||||||
text-variant="default"
|
class="w-full bg-n-alpha-2 n-blue-text hover:bg-n-alpha-3"
|
||||||
class="w-full bg-n-alpha-2 hover:bg-n-alpha-3"
|
|
||||||
@click="handleCancel"
|
@click="handleCancel"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -133,10 +133,9 @@ const handleBreadcrumbClick = () => {
|
|||||||
<Button
|
<Button
|
||||||
:label="activeLocaleName"
|
:label="activeLocaleName"
|
||||||
size="sm"
|
size="sm"
|
||||||
icon-position="right"
|
trailing-icon
|
||||||
icon="chevron-lucide-down"
|
icon="i-lucide-chevron-down"
|
||||||
icon-lib="lucide"
|
color="slate"
|
||||||
variant="secondary"
|
|
||||||
@click="isLocaleMenuOpen = !isLocaleMenuOpen"
|
@click="isLocaleMenuOpen = !isLocaleMenuOpen"
|
||||||
/>
|
/>
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
@@ -165,7 +164,7 @@ const handleBreadcrumbClick = () => {
|
|||||||
<OnClickOutside @trigger="isCreateCategoryDialogOpen = false">
|
<OnClickOutside @trigger="isCreateCategoryDialogOpen = false">
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.NEW_CATEGORY')"
|
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.NEW_CATEGORY')"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="isCreateCategoryDialogOpen = !isCreateCategoryDialogOpen"
|
@click="isCreateCategoryDialogOpen = !isCreateCategoryDialogOpen"
|
||||||
/>
|
/>
|
||||||
@@ -183,7 +182,7 @@ const handleBreadcrumbClick = () => {
|
|||||||
<OnClickOutside @trigger="isEditCategoryDialogOpen = false">
|
<OnClickOutside @trigger="isEditCategoryDialogOpen = false">
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.EDIT_CATEGORY')"
|
:label="t('HELP_CENTER.CATEGORY_PAGE.CATEGORY_HEADER.EDIT_CATEGORY')"
|
||||||
variant="secondary"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="isEditCategoryDialogOpen = !isEditCategoryDialogOpen"
|
@click="isEditCategoryDialogOpen = !isEditCategoryDialogOpen"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const localeCount = computed(() => props.locales?.length);
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
:label="$t('HELP_CENTER.LOCALES_PAGE.NEW_LOCALE_BUTTON_TEXT')"
|
:label="$t('HELP_CENTER.LOCALES_PAGE.NEW_LOCALE_BUTTON_TEXT')"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="openAddLocaleDialog"
|
@click="openAddLocaleDialog"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -196,16 +196,17 @@ const handleAvatarDelete = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full gap-4">
|
<div class="flex flex-col w-full gap-4">
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] py-2.5 text-slate-900 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap py-2.5 text-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.NAME.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.NAME.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
v-model="state.name"
|
v-model="state.name"
|
||||||
:placeholder="t('HELP_CENTER.PORTAL_SETTINGS.FORM.NAME.PLACEHOLDER')"
|
:placeholder="t('HELP_CENTER.PORTAL_SETTINGS.FORM.NAME.PLACEHOLDER')"
|
||||||
class="w-[432px]"
|
|
||||||
:message-type="nameError ? 'error' : 'info'"
|
:message-type="nameError ? 'error' : 'info'"
|
||||||
:message="nameError"
|
:message="nameError"
|
||||||
custom-input-class="!bg-transparent dark:!bg-transparent"
|
custom-input-class="!bg-transparent dark:!bg-transparent"
|
||||||
@@ -213,9 +214,11 @@ const handleAvatarDelete = () => {
|
|||||||
@blur="v$.name.$touch()"
|
@blur="v$.name.$touch()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] py-2.5 text-slate-900 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap py-2.5 text-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.HEADER_TEXT.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.HEADER_TEXT.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -224,13 +227,14 @@ const handleAvatarDelete = () => {
|
|||||||
:placeholder="
|
:placeholder="
|
||||||
t('HELP_CENTER.PORTAL_SETTINGS.FORM.HEADER_TEXT.PLACEHOLDER')
|
t('HELP_CENTER.PORTAL_SETTINGS.FORM.HEADER_TEXT.PLACEHOLDER')
|
||||||
"
|
"
|
||||||
class="w-[432px]"
|
|
||||||
custom-input-class="!bg-transparent dark:!bg-transparent"
|
custom-input-class="!bg-transparent dark:!bg-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] text-slate-900 py-2.5 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap text-slate-900 py-2.5 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.PAGE_TITLE.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.PAGE_TITLE.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -239,13 +243,14 @@ const handleAvatarDelete = () => {
|
|||||||
:placeholder="
|
:placeholder="
|
||||||
t('HELP_CENTER.PORTAL_SETTINGS.FORM.PAGE_TITLE.PLACEHOLDER')
|
t('HELP_CENTER.PORTAL_SETTINGS.FORM.PAGE_TITLE.PLACEHOLDER')
|
||||||
"
|
"
|
||||||
class="w-[432px]"
|
|
||||||
custom-input-class="!bg-transparent dark:!bg-transparent"
|
custom-input-class="!bg-transparent dark:!bg-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] text-slate-900 py-2.5 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap text-slate-900 py-2.5 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.HOME_PAGE_LINK.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.HOME_PAGE_LINK.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -254,7 +259,6 @@ const handleAvatarDelete = () => {
|
|||||||
:placeholder="
|
:placeholder="
|
||||||
t('HELP_CENTER.PORTAL_SETTINGS.FORM.HOME_PAGE_LINK.PLACEHOLDER')
|
t('HELP_CENTER.PORTAL_SETTINGS.FORM.HOME_PAGE_LINK.PLACEHOLDER')
|
||||||
"
|
"
|
||||||
class="w-[432px]"
|
|
||||||
:message-type="homePageLinkError ? 'error' : 'info'"
|
:message-type="homePageLinkError ? 'error' : 'info'"
|
||||||
:message="homePageLinkError"
|
:message="homePageLinkError"
|
||||||
custom-input-class="!bg-transparent dark:!bg-transparent"
|
custom-input-class="!bg-transparent dark:!bg-transparent"
|
||||||
@@ -262,16 +266,17 @@ const handleAvatarDelete = () => {
|
|||||||
@blur="v$.homePageLink.$touch()"
|
@blur="v$.homePageLink.$touch()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] py-2.5 text-slate-900 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap py-2.5 text-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.SLUG.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.SLUG.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<Input
|
||||||
v-model="state.slug"
|
v-model="state.slug"
|
||||||
:placeholder="t('HELP_CENTER.PORTAL_SETTINGS.FORM.SLUG.PLACEHOLDER')"
|
:placeholder="t('HELP_CENTER.PORTAL_SETTINGS.FORM.SLUG.PLACEHOLDER')"
|
||||||
class="w-[432px]"
|
|
||||||
:message-type="slugError ? 'error' : 'info'"
|
:message-type="slugError ? 'error' : 'info'"
|
||||||
:message="slugError || buildPortalURL(state.slug)"
|
:message="slugError || buildPortalURL(state.slug)"
|
||||||
custom-input-class="!bg-transparent dark:!bg-transparent"
|
custom-input-class="!bg-transparent dark:!bg-transparent"
|
||||||
@@ -279,9 +284,11 @@ const handleAvatarDelete = () => {
|
|||||||
@blur="v$.slug.$touch()"
|
@blur="v$.slug.$touch()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div
|
||||||
|
class="grid items-start justify-between w-full gap-2 grid-cols-[200px,1fr]"
|
||||||
|
>
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] py-2.5 text-slate-900 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap py-2.5 text-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.LIVE_CHAT_WIDGET.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.LIVE_CHAT_WIDGET.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -294,12 +301,12 @@ const handleAvatarDelete = () => {
|
|||||||
:message="
|
:message="
|
||||||
t('HELP_CENTER.PORTAL_SETTINGS.FORM.LIVE_CHAT_WIDGET.HELP_TEXT')
|
t('HELP_CENTER.PORTAL_SETTINGS.FORM.LIVE_CHAT_WIDGET.HELP_TEXT')
|
||||||
"
|
"
|
||||||
class="[&>button]:w-[432px] !w-[432px]"
|
class="[&>div>button]:!outline-n-weak"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start justify-between w-full gap-2">
|
<div class="flex items-start justify-between w-full gap-2">
|
||||||
<label
|
<label
|
||||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] py-2.5 text-slate-900 dark:text-slate-50"
|
class="text-sm font-medium whitespace-nowrap py-2.5 text-slate-900 dark:text-slate-50"
|
||||||
>
|
>
|
||||||
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.BRAND_COLOR.LABEL') }}
|
{{ t('HELP_CENTER.PORTAL_SETTINGS.FORM.BRAND_COLOR.LABEL') }}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -82,12 +82,12 @@ const closeDNSConfigurationDialog = () => {
|
|||||||
<div class="flex items-center justify-end w-full">
|
<div class="flex items-center justify-end w-full">
|
||||||
<Button
|
<Button
|
||||||
v-if="customDomainAddress"
|
v-if="customDomainAddress"
|
||||||
|
color="slate"
|
||||||
:label="
|
:label="
|
||||||
t(
|
t(
|
||||||
'HELP_CENTER.PORTAL_SETTINGS.CONFIGURATION_FORM.CUSTOM_DOMAIN.EDIT_BUTTON'
|
'HELP_CENTER.PORTAL_SETTINGS.CONFIGURATION_FORM.CUSTOM_DOMAIN.EDIT_BUTTON'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
variant="secondary"
|
|
||||||
@click="addCustomDomainDialogRef.dialogRef.open()"
|
@click="addCustomDomainDialogRef.dialogRef.open()"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -97,7 +97,7 @@ const closeDNSConfigurationDialog = () => {
|
|||||||
'HELP_CENTER.PORTAL_SETTINGS.CONFIGURATION_FORM.CUSTOM_DOMAIN.ADD_BUTTON'
|
'HELP_CENTER.PORTAL_SETTINGS.CONFIGURATION_FORM.CUSTOM_DOMAIN.ADD_BUTTON'
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
variant="secondary"
|
color="slate"
|
||||||
@click="addCustomDomainDialogRef.dialogRef.open()"
|
@click="addCustomDomainDialogRef.dialogRef.open()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ const handleDeletePortal = () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
variant="destructive"
|
color="ruby"
|
||||||
class="w-56"
|
class="max-w-56 !w-fit"
|
||||||
@click="openConfirmDeletePortalDialog"
|
@click="openConfirmDeletePortalDialog"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -120,8 +120,8 @@ const redirectToPortalHomePage = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
:label="t('HELP_CENTER.PORTAL_SWITCHER.NEW_PORTAL')"
|
:label="t('HELP_CENTER.PORTAL_SWITCHER.NEW_PORTAL')"
|
||||||
variant="secondary"
|
color="slate"
|
||||||
icon="add"
|
icon="i-lucide-plus"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="!bg-n-alpha-2 hover:!bg-n-alpha-3"
|
class="!bg-n-alpha-2 hover:!bg-n-alpha-3"
|
||||||
@click="openCreatePortalDialog"
|
@click="openCreatePortalDialog"
|
||||||
@@ -133,29 +133,30 @@ const redirectToPortalHomePage = () => {
|
|||||||
:key="index"
|
:key="index"
|
||||||
:label="portal.name"
|
:label="portal.name"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
:icon="isPortalActive(portal) ? 'checkmark-lucide' : ''"
|
trailing-icon
|
||||||
icon-lib="lucide"
|
:icon="isPortalActive(portal) ? 'i-lucide-check' : ''"
|
||||||
icon-position="right"
|
class="!justify-end !px-2 !py-2 hover:!bg-n-alpha-2 [&>.i-lucide-check]:text-n-teal-10 h-9"
|
||||||
class="!justify-start !px-2 !py-2 hover:!bg-n-alpha-2 [&>svg]:text-n-teal-10 [&>svg]:w-5 [&>svg]:h-5 h-9"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="handlePortalChange(portal)"
|
@click="handlePortalChange(portal)"
|
||||||
>
|
>
|
||||||
<template #leftPrefix>
|
<div v-if="portal.custom_domain" class="flex items-center gap-1">
|
||||||
<Thumbnail
|
<span class="i-lucide-link size-3" />
|
||||||
v-if="portal"
|
|
||||||
:author="portal"
|
|
||||||
:name="portal.name"
|
|
||||||
:size="20"
|
|
||||||
:src="getPortalThumbnailSrc(portal)"
|
|
||||||
:show-author-name="false"
|
|
||||||
icon-name="building-lucide"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template #rightPrefix>
|
|
||||||
<span class="text-sm truncate text-n-slate-11">
|
<span class="text-sm truncate text-n-slate-11">
|
||||||
{{ portal.custom_domain || '' }}
|
{{ portal.custom_domain || '' }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</div>
|
||||||
|
<span class="text-sm font-medium truncate text-n-slate-12">
|
||||||
|
{{ portal.name || '' }}
|
||||||
|
</span>
|
||||||
|
<Thumbnail
|
||||||
|
v-if="portal"
|
||||||
|
:author="portal"
|
||||||
|
:name="portal.name"
|
||||||
|
:size="20"
|
||||||
|
:src="getPortalThumbnailSrc(portal)"
|
||||||
|
:show-author-name="false"
|
||||||
|
icon-name="i-lucide-building-2"
|
||||||
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
src: {
|
src: {
|
||||||
@@ -57,7 +57,7 @@ const handleDismiss = event => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="relative flex flex-col items-center gap-2 select-none rounded-xl group/avatar"
|
class="relative flex flex-col items-center gap-2 select-none rounded-xl outline outline-1 outline-n-container group/avatar"
|
||||||
:style="{ width: avatarSize, height: avatarSize }"
|
:style="{ width: avatarSize, height: avatarSize }"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@@ -71,29 +71,27 @@ const handleDismiss = event => {
|
|||||||
v-else
|
v-else
|
||||||
class="flex items-center justify-center w-full h-full rounded-xl bg-n-alpha-2"
|
class="flex items-center justify-center w-full h-full rounded-xl bg-n-alpha-2"
|
||||||
>
|
>
|
||||||
<FluentIcon
|
<Icon
|
||||||
icon="building-lucide"
|
icon="i-lucide-building-2"
|
||||||
icon-lib="lucide"
|
class="text-n-brand/50"
|
||||||
:size="iconSize"
|
:style="{ width: `${iconSize}`, height: `${iconSize}` }"
|
||||||
class="dark:text-n-brand/50 text-n-brand/30"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="src"
|
v-if="src"
|
||||||
class="absolute z-20 flex items-center cursor-pointer justify-center w-6 h-6 transition-all invisible opacity-0 duration-500 ease-in-out -top-2.5 -right-2.5 rounded-xl bg-n-solid-3 group-hover/avatar:visible group-hover/avatar:opacity-100"
|
class="absolute z-20 outline outline-1 outline-n-container flex items-center cursor-pointer justify-center w-6 h-6 transition-all invisible opacity-0 duration-500 ease-in-out -top-2.5 -right-2.5 rounded-xl bg-n-solid-3 group-hover/avatar:visible group-hover/avatar:opacity-100"
|
||||||
@click="handleDismiss"
|
@click="handleDismiss"
|
||||||
>
|
>
|
||||||
<FluentIcon icon="dismiss" :size="16" class="text-n-slate-11" />
|
<Icon icon="i-lucide-x" class="text-n-slate-11 size-4" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-0 z-10 flex items-center justify-center invisible w-full h-full transition-all duration-500 ease-in-out opacity-0 rounded-xl bg-n-alpha-black1 group-hover/avatar:visible group-hover/avatar:opacity-100"
|
class="absolute inset-0 z-10 flex items-center justify-center invisible w-full h-full transition-all duration-500 ease-in-out opacity-0 rounded-xl bg-n-alpha-black1 group-hover/avatar:visible group-hover/avatar:opacity-100"
|
||||||
@click="handleUploadAvatar"
|
@click="handleUploadAvatar"
|
||||||
>
|
>
|
||||||
<FluentIcon
|
<Icon
|
||||||
icon="upload-lucide"
|
icon="i-lucide-upload"
|
||||||
icon-lib="lucide"
|
class="text-white"
|
||||||
:size="iconSize"
|
:style="{ width: `${iconSize}`, height: `${iconSize}` }"
|
||||||
class="text-white dark:text-white"
|
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
ref="fileInput"
|
ref="fileInput"
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
import { defineProps } from 'vue';
|
import { defineProps } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
import Button from 'dashboard/components-next/button/Button.vue';
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
items: {
|
items: {
|
||||||
@@ -33,21 +32,17 @@ const onClick = event => {
|
|||||||
<nav :aria-label="t('BREADCRUMB.ARIA_LABEL')" class="flex items-center h-8">
|
<nav :aria-label="t('BREADCRUMB.ARIA_LABEL')" class="flex items-center h-8">
|
||||||
<ol class="flex items-center mb-0">
|
<ol class="flex items-center mb-0">
|
||||||
<li v-for="(item, index) in items" :key="index" class="flex items-center">
|
<li v-for="(item, index) in items" :key="index" class="flex items-center">
|
||||||
<Button
|
<button
|
||||||
v-if="index === 0"
|
v-if="index === 0"
|
||||||
:label="item.label"
|
class="inline-flex items-center justify-center min-w-0 gap-2 p-0 text-sm font-medium transition-all duration-200 ease-in-out border-0 rounded-lg text-n-slate-11 hover:text-n-slate-12 outline-transparent max-w-56"
|
||||||
variant="link"
|
|
||||||
text-variant="info"
|
|
||||||
class="!p-0 text-sm !font-normal hover:!no-underline max-w-56 !text-slate-300 dark:!text-slate-500 hover:!text-slate-700 dark:hover:!text-slate-100"
|
|
||||||
size="sm"
|
|
||||||
@click="onClick"
|
@click="onClick"
|
||||||
/>
|
>
|
||||||
|
<span class="min-w-0 truncate">{{ item.label }}</span>
|
||||||
|
</button>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<FluentIcon
|
<Icon
|
||||||
icon="chevron-lucide-right"
|
icon="i-lucide-chevron-right"
|
||||||
size="18"
|
class="flex-shrink-0 mx-2 size-4 text-n-slate-11 dark:text-n-slate-11"
|
||||||
icon-lib="lucide"
|
|
||||||
class="flex-shrink-0 mx-2 text-slate-300 dark:text-slate-500"
|
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="text-sm truncate text-slate-900 dark:text-slate-50 max-w-56"
|
class="text-sm truncate text-slate-900 dark:text-slate-50 max-w-56"
|
||||||
|
|||||||
@@ -1,125 +1,123 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Button from './Button.vue';
|
import Button from './Button.vue';
|
||||||
|
|
||||||
|
// Constants for documentation
|
||||||
|
const VARIANTS = ['solid', 'outline', 'faded', 'link', 'ghost'];
|
||||||
|
const COLORS = ['blue', 'ruby', 'amber', 'slate', 'teal'];
|
||||||
|
const SIZES = ['default', 'sm', 'lg'];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Story title="Components/Button" :layout="{ type: 'grid', width: '400' }">
|
<Story title="Components/Button" :layout="{ type: 'grid', width: '800px' }">
|
||||||
<Variant title="Default">
|
<!-- Basic Variants -->
|
||||||
<div class="p-4 bg-white dark:bg-slate-900">
|
<Variant title="Basic Variants">
|
||||||
<Button label="Default Button" />
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
|
<Button
|
||||||
|
v-for="variant in VARIANTS"
|
||||||
|
:key="variant"
|
||||||
|
:label="variant"
|
||||||
|
:variant="variant"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Variant>
|
</Variant>
|
||||||
|
|
||||||
<Variant title="Disabled">
|
<!-- Colors -->
|
||||||
|
<Variant title="Color Variants">
|
||||||
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
|
<Button
|
||||||
|
v-for="color in COLORS"
|
||||||
|
:key="color"
|
||||||
|
:label="color"
|
||||||
|
:color="color"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<!-- Sizes -->
|
||||||
|
<Variant title="Size Variants">
|
||||||
|
<div
|
||||||
|
class="flex flex-wrap items-center gap-2 p-4 bg-white dark:bg-slate-900"
|
||||||
|
>
|
||||||
|
<Button v-for="size in SIZES" :key="size" :label="size" :size="size" />
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<!-- Icons -->
|
||||||
|
<Variant title="Icons">
|
||||||
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
|
<Button label="Leading Icon" icon="i-lucide-plus" />
|
||||||
|
<Button label="Trailing Icon" icon="i-lucide-plus" trailing-icon />
|
||||||
|
<Button icon="i-lucide-plus" />
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<!-- Loading State -->
|
||||||
|
<Variant title="Loading State">
|
||||||
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
|
<Button label="Loading" is-loading />
|
||||||
|
<Button label="Loading" variant="outline" is-loading />
|
||||||
|
<Button is-loading icon="i-lucide-plus" />
|
||||||
|
</div>
|
||||||
|
</Variant>
|
||||||
|
|
||||||
|
<!-- Disabled State -->
|
||||||
|
<Variant title="Disabled State">
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
<Button label="Disabled" disabled />
|
<Button label="Disabled" disabled />
|
||||||
<Button label="Disabled" variant="outline" disabled />
|
<Button label="Disabled Outline" variant="outline" disabled />
|
||||||
<Button label="Disabled" disabled icon="delete" variant="outline" />
|
<Button label="Disabled Icon" icon="delete" disabled />
|
||||||
<Button
|
<Button
|
||||||
label="Disabled"
|
label="Disabled Destructive"
|
||||||
|
color="ruby"
|
||||||
disabled
|
disabled
|
||||||
icon="delete"
|
icon="delete"
|
||||||
variant="destructive"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label="Disabled"
|
|
||||||
disabled
|
|
||||||
icon="delete"
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label="Disabled"
|
|
||||||
disabled
|
|
||||||
icon="delete"
|
|
||||||
variant="link"
|
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Variant>
|
</Variant>
|
||||||
|
|
||||||
<Variant title="Disabled with icon">
|
<!-- Color Combinations -->
|
||||||
<div class="p-4 bg-white dark:bg-slate-900">
|
<Variant title="Color & Variant Combinations">
|
||||||
<Button label="Disabled Button" icon="emoji-add" disabled />
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
|
<template v-for="color in COLORS" :key="color">
|
||||||
|
<Button
|
||||||
|
v-for="variant in VARIANTS"
|
||||||
|
:key="`${color}-${variant}`"
|
||||||
|
:label="`${color} ${variant}`"
|
||||||
|
:color="color"
|
||||||
|
:variant="variant"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Variant>
|
</Variant>
|
||||||
|
|
||||||
<Variant title="Different variant">
|
<!-- Icon Positions -->
|
||||||
|
<Variant title="Icon Positions & Sizes">
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
<Button label="Default" variant="default" />
|
<template v-for="size in SIZES" :key="size">
|
||||||
<Button label="Destructive" variant="destructive" />
|
<Button
|
||||||
<Button label="Outline" variant="outline" />
|
:label="`${size} Leading`"
|
||||||
<Button label="Secondary" variant="secondary" />
|
icon="i-lucide-plus"
|
||||||
<Button label="Ghost" variant="ghost" />
|
:size="size"
|
||||||
<Button label="Link" variant="link" />
|
/>
|
||||||
|
<Button
|
||||||
|
:label="`${size} Trailing`"
|
||||||
|
icon="i-lucide-plus"
|
||||||
|
trailing-icon
|
||||||
|
:size="size"
|
||||||
|
/>
|
||||||
|
<Button icon="i-lucide-plus" :size="size" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</Variant>
|
</Variant>
|
||||||
|
|
||||||
<Variant title="Different variant with icon only">
|
<!-- Ghost & Link Variants -->
|
||||||
|
<Variant title="Ghost & Link Variants">
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
||||||
<Button icon="emoji-add" variant="default" />
|
<Button label="Ghost Button" variant="ghost" />
|
||||||
<Button icon="emoji-add" variant="destructive" />
|
<Button label="Ghost with Icon" variant="ghost" icon="i-lucide-plus" />
|
||||||
<Button icon="emoji-add" variant="outline" />
|
<Button label="Link Button" variant="link" />
|
||||||
<Button icon="emoji-add" variant="secondary" />
|
<Button label="Link with Icon" variant="link" icon="i-lucide-plus" />
|
||||||
<Button icon="emoji-add" variant="ghost" />
|
|
||||||
<Button icon="emoji-add" variant="link" />
|
|
||||||
</div>
|
|
||||||
</Variant>
|
|
||||||
|
|
||||||
<Variant title="Different size">
|
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
|
||||||
<Button label="Default" />
|
|
||||||
<Button label="Large" size="lg" />
|
|
||||||
<Button label="Small" size="sm" />
|
|
||||||
</div>
|
|
||||||
</Variant>
|
|
||||||
|
|
||||||
<Variant title="Different text variant">
|
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
|
||||||
<Button label="Default" text-variant="default" variant="outline" />
|
|
||||||
<Button label="Success" text-variant="success" variant="outline" />
|
|
||||||
<Button label="Warning" text-variant="warning" variant="outline" />
|
|
||||||
<Button label="Danger" text-variant="danger" variant="outline" />
|
|
||||||
<Button label="Info" text-variant="info" variant="outline" />
|
|
||||||
</div>
|
|
||||||
</Variant>
|
|
||||||
|
|
||||||
<Variant title="Button with left icon with different sizes and icon only">
|
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
|
||||||
<Button label="Default" icon="emoji-add" icon-position="left" />
|
|
||||||
<Button
|
|
||||||
label="Default LG"
|
|
||||||
icon="emoji-add"
|
|
||||||
icon-position="left"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label="Default SM"
|
|
||||||
icon="emoji-add"
|
|
||||||
icon-position="left"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<Button icon="emoji-add" size="sm" />
|
|
||||||
</div>
|
|
||||||
</Variant>
|
|
||||||
|
|
||||||
<Variant title="Button with right icon with different sizes and icon only">
|
|
||||||
<div class="flex flex-wrap gap-2 p-4 bg-white dark:bg-slate-900">
|
|
||||||
<Button label="Default" icon="emoji-add" icon-position="right" />
|
|
||||||
<Button
|
|
||||||
label="Default LG"
|
|
||||||
icon="emoji-add"
|
|
||||||
icon-position="right"
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
label="Default SM"
|
|
||||||
icon="emoji-add"
|
|
||||||
icon-position="right"
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
<Button icon="emoji-add" size="sm" />
|
|
||||||
</div>
|
</div>
|
||||||
</Variant>
|
</Variant>
|
||||||
</Story>
|
</Story>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed, useSlots } from 'vue';
|
||||||
|
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||||
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
label: {
|
label: {
|
||||||
@@ -11,49 +11,28 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
variant: {
|
variant: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'default',
|
default: 'solid',
|
||||||
validator: value =>
|
validator: value =>
|
||||||
[
|
['solid', 'outline', 'faded', 'link', 'ghost'].includes(value),
|
||||||
'default',
|
|
||||||
'destructive',
|
|
||||||
'outline',
|
|
||||||
'secondary',
|
|
||||||
'ghost',
|
|
||||||
'link',
|
|
||||||
].includes(value),
|
|
||||||
},
|
},
|
||||||
textVariant: {
|
color: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: 'blue',
|
||||||
validator: value =>
|
validator: value =>
|
||||||
['', 'default', 'success', 'warning', 'danger', 'info'].includes(value),
|
['blue', 'ruby', 'amber', 'slate', 'teal'].includes(value),
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'default',
|
default: 'md',
|
||||||
validator: value => ['default', 'sm', 'lg'].includes(value),
|
validator: value => ['xs', 'sm', 'md', 'lg'].includes(value),
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'button',
|
|
||||||
validator: value => ['button', 'submit', 'reset'].includes(value),
|
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
emoji: {
|
trailingIcon: {
|
||||||
type: String,
|
type: Boolean,
|
||||||
default: '',
|
default: false,
|
||||||
},
|
|
||||||
iconPosition: {
|
|
||||||
type: String,
|
|
||||||
default: 'left',
|
|
||||||
validator: value => ['left', 'right'].includes(value),
|
|
||||||
},
|
|
||||||
iconLib: {
|
|
||||||
type: String,
|
|
||||||
default: 'fluent',
|
|
||||||
},
|
},
|
||||||
isLoading: {
|
isLoading: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -61,92 +40,128 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['click']);
|
const slots = useSlots();
|
||||||
|
|
||||||
const buttonVariants = {
|
const STYLE_CONFIG = {
|
||||||
variant: {
|
colors: {
|
||||||
default:
|
blue: {
|
||||||
'bg-n-brand text-white dark:text-white hover:bg-woot-600 dark:hover:bg-woot-600',
|
solid: 'bg-n-brand text-white hover:bg-n-blue-text outline-transparent',
|
||||||
destructive: 'bg-n-ruby-9 text-white dark:text-white hover:bg-n-ruby-10',
|
faded:
|
||||||
outline:
|
'bg-n-brand/10 text-n-slate-12 hover:bg-n-brand/20 outline-transparent',
|
||||||
'border border-n-weak dark:border-n-weak hover:border-n-slate-6 dark:hover:border-n-slate-6',
|
outline: 'text-n-blue-text hover:bg-n-brand/10 outline-n-blue-border',
|
||||||
secondary: 'bg-n-solid-3 text-n-slate-12 hover:bg-n-solid-2',
|
link: 'text-n-brand hover:underline outline-transparent',
|
||||||
ghost: 'text-n-slate-12',
|
},
|
||||||
link: 'text-n-brand underline-offset-4 hover:underline dark:hover:underline',
|
ruby: {
|
||||||
|
solid: 'bg-n-ruby-9 text-white hover:bg-n-ruby-10 outline-transparent',
|
||||||
|
faded:
|
||||||
|
'bg-n-ruby-9/10 text-n-slate-12 hover:bg-n-ruby-9/20 outline-transparent',
|
||||||
|
outline: 'text-n-ruby-11 hover:bg-n-ruby-9/10 outline-n-ruby-9',
|
||||||
|
link: 'text-n-ruby-9 hover:underline outline-transparent',
|
||||||
|
},
|
||||||
|
amber: {
|
||||||
|
solid: 'bg-n-amber-9 text-white hover:bg-n-amber-10 outline-transparent',
|
||||||
|
faded:
|
||||||
|
'bg-n-amber-9/10 text-n-slate-12 hover:bg-n-amber-9/20 outline-transparent',
|
||||||
|
outline: 'text-n-amber-11 hover:bg-n-amber-9/10 outline-n-amber-9',
|
||||||
|
link: 'text-n-amber-9 hover:underline outline-transparent',
|
||||||
|
},
|
||||||
|
slate: {
|
||||||
|
solid:
|
||||||
|
'bg-n-solid-3 hover:bg-n-solid-2 text-n-slate-12 outline-n-container',
|
||||||
|
faded:
|
||||||
|
'bg-n-slate-9/10 text-n-slate-12 hover:bg-n-slate-9/20 outline-transparent',
|
||||||
|
outline: 'text-n-slate-11 outline-n-strong hover:bg-n-slate-9/10',
|
||||||
|
link: 'text-n-slate-11 hover:text-n-slate-12 hover:underline outline-transparent',
|
||||||
|
},
|
||||||
|
teal: {
|
||||||
|
solid: 'bg-n-teal-9 text-white hover:bg-n-teal-10 outline-transparent',
|
||||||
|
faded:
|
||||||
|
'bg-n-teal-9/10 text-n-slate-12 hover:bg-n-teal-9/20 outline-transparent',
|
||||||
|
outline: 'text-n-teal-11 hover:bg-n-teal-9/10 outline-n-teal-9',
|
||||||
|
link: 'text-n-teal-9 hover:underline outline-transparent',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
size: {
|
sizes: {
|
||||||
default: 'h-10 px-4 py-2',
|
regular: {
|
||||||
sm: 'h-8 px-3 py-1',
|
xs: 'h-6 px-2',
|
||||||
lg: 'h-12 px-5 py-3',
|
sm: 'h-8 px-3',
|
||||||
|
md: 'h-10 px-4',
|
||||||
|
lg: 'h-12 px-5',
|
||||||
|
},
|
||||||
|
iconOnly: {
|
||||||
|
xs: 'h-6 w-6 p-0',
|
||||||
|
sm: 'h-8 w-8 p-0',
|
||||||
|
md: 'h-10 w-10 p-0',
|
||||||
|
lg: 'h-12 w-12 p-0',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
xs: 'p-0',
|
||||||
|
sm: 'p-0',
|
||||||
|
md: 'p-0',
|
||||||
|
lg: 'p-0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
text: {
|
fontSize: {
|
||||||
default:
|
xs: 'text-xs',
|
||||||
'!text-n-brand dark:!text-n-brand hover:!text-woot-600 dark:hover:!text-woot-600',
|
sm: 'text-sm',
|
||||||
success:
|
md: 'text-sm font-medium',
|
||||||
'!text-green-500 dark:!text-green-500 hover:!text-green-600 dark:hover:!text-green-600',
|
lg: 'text-base',
|
||||||
warning:
|
|
||||||
'!text-amber-600 dark:!text-amber-600 hover:!text-amber-600 dark:hover:!text-amber-600',
|
|
||||||
danger: '!text-n-ruby-11 hover:!text-n-ruby-10',
|
|
||||||
info: '!text-n-slate-12 hover:!text-n-slate-11',
|
|
||||||
},
|
},
|
||||||
|
base: 'inline-flex items-center justify-center min-w-0 gap-2 transition-all duration-200 ease-in-out border-0 rounded-lg outline-1 outline disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-50',
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttonClasses = computed(() => {
|
const variantClasses = computed(() => {
|
||||||
const classes = [
|
const variantMap = {
|
||||||
buttonVariants.variant[props.variant],
|
ghost: 'text-n-slate-12 hover:bg-n-alpha-2 outline-transparent',
|
||||||
buttonVariants.size[props.size],
|
link: `${STYLE_CONFIG.colors[props.color].link} p-0 font-medium underline-offset-4`,
|
||||||
];
|
outline: STYLE_CONFIG.colors[props.color].outline,
|
||||||
|
faded: STYLE_CONFIG.colors[props.color].faded,
|
||||||
|
solid: STYLE_CONFIG.colors[props.color].solid,
|
||||||
|
};
|
||||||
|
|
||||||
if (props.textVariant && buttonVariants.text[props.textVariant]) {
|
return variantMap[props.variant];
|
||||||
classes.push(buttonVariants.text[props.textVariant]);
|
});
|
||||||
}
|
|
||||||
|
const isIconOnly = computed(() => !props.label && !slots.default);
|
||||||
|
const isLink = computed(() => props.variant === 'link');
|
||||||
|
|
||||||
|
const buttonClasses = computed(() => {
|
||||||
|
const sizeConfig = isIconOnly.value ? 'iconOnly' : 'regular';
|
||||||
|
const classes = [
|
||||||
|
variantClasses.value,
|
||||||
|
props.variant !== 'link' && STYLE_CONFIG.sizes[sizeConfig][props.size],
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
return classes.join(' ');
|
return classes.join(' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
const iconSize = computed(() => {
|
const linkButtonClasses = computed(() => {
|
||||||
if (props.size === 'sm') return 16;
|
const classes = [
|
||||||
if (props.size === 'lg') return 20;
|
variantClasses.value,
|
||||||
return 18;
|
STYLE_CONFIG.sizes.link[props.size],
|
||||||
});
|
].filter(Boolean);
|
||||||
|
|
||||||
const handleClick = e => {
|
return classes.join(' ');
|
||||||
emit('click', e);
|
});
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
:class="buttonClasses"
|
:class="{
|
||||||
:type="type"
|
[STYLE_CONFIG.base]: true,
|
||||||
class="inline-flex items-center justify-center min-w-0 gap-2 text-sm font-medium transition-all duration-200 ease-in-out rounded-lg disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-50"
|
[isLink ? linkButtonClasses : buttonClasses]: true,
|
||||||
@click="handleClick"
|
[STYLE_CONFIG.fontSize[size]]: true,
|
||||||
|
'flex-row-reverse': trailingIcon && !isIconOnly,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<FluentIcon
|
<slot v-if="(icon || $slots.icon) && !isLoading" name="icon">
|
||||||
v-if="icon && iconPosition === 'left' && !isLoading"
|
<Icon :icon="icon" class="flex-shrink-0" />
|
||||||
:icon="icon"
|
</slot>
|
||||||
:size="iconSize"
|
|
||||||
:icon-lib="iconLib"
|
|
||||||
class="flex-shrink-0"
|
|
||||||
:class="{
|
|
||||||
'text-n-slate-11 dark:text-n-slate-11': variant === 'secondary',
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
<Spinner v-if="isLoading" class="!w-5 !h-5 flex-shrink-0" />
|
<Spinner v-if="isLoading" class="!w-5 !h-5 flex-shrink-0" />
|
||||||
<slot name="leftPrefix" />
|
|
||||||
<span v-if="emoji">{{ emoji }}</span>
|
<slot v-if="label || $slots.default" name="default">
|
||||||
<span v-if="label" class="min-w-0 truncate">{{ label }}</span>
|
<span class="min-w-0 truncate">{{ label }}</span>
|
||||||
<slot />
|
</slot>
|
||||||
<slot name="rightPrefix" />
|
|
||||||
<FluentIcon
|
|
||||||
v-if="icon && iconPosition === 'right'"
|
|
||||||
:icon="icon"
|
|
||||||
:size="iconSize"
|
|
||||||
:icon-lib="iconLib"
|
|
||||||
class="flex-shrink-0"
|
|
||||||
:class="{
|
|
||||||
'text-n-slate-11 dark:text-n-slate-11': variant === 'secondary',
|
|
||||||
}"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -37,20 +37,19 @@ const pickerRef = ref(null);
|
|||||||
<div ref="pickerRef" class="relative w-fit">
|
<div ref="pickerRef" class="relative w-fit">
|
||||||
<OnClickOutside @trigger="closeTogglePicker">
|
<OnClickOutside @trigger="closeTogglePicker">
|
||||||
<Button
|
<Button
|
||||||
:label="modelValue"
|
color="slate"
|
||||||
variant="secondary"
|
icon="i-lucide-pipette"
|
||||||
icon-lib="lucide"
|
trailing-icon
|
||||||
icon-position="right"
|
|
||||||
icon="pipette-lucide"
|
|
||||||
class="!px-3 !py-3 [&>svg]:w-4 [&>svg]:h-4"
|
class="!px-3 !py-3 [&>svg]:w-4 [&>svg]:h-4"
|
||||||
@click="toggleColorPicker"
|
@click="toggleColorPicker"
|
||||||
>
|
>
|
||||||
<template #leftPrefix>
|
<div class="flex items-center flex-grow gap-2">
|
||||||
<div
|
<span
|
||||||
class="w-4 h-4 rounded-sm"
|
class="rounded-md size-4"
|
||||||
:style="{ backgroundColor: modelValue }"
|
:style="{ backgroundColor: modelValue }"
|
||||||
/>
|
/>
|
||||||
</template>
|
<span class="min-w-0 truncate">{{ modelValue }}</span>
|
||||||
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
<Chrome
|
<Chrome
|
||||||
v-if="isPickerOpen"
|
v-if="isPickerOpen"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { ref, computed, watch, nextTick } from 'vue';
|
import { ref, computed, watch, nextTick } from 'vue';
|
||||||
import { OnClickOutside } from '@vueuse/components';
|
import { OnClickOutside } from '@vueuse/components';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
import Button from 'dashboard/components-next/button/Button.vue';
|
import Button from 'dashboard/components-next/button/Button.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -89,7 +89,7 @@ watch(
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
ref="comboboxRef"
|
ref="comboboxRef"
|
||||||
class="relative w-full"
|
class="relative w-full min-w-0"
|
||||||
:class="{
|
:class="{
|
||||||
'cursor-not-allowed': disabled,
|
'cursor-not-allowed': disabled,
|
||||||
'group/combobox': !disabled,
|
'group/combobox': !disabled,
|
||||||
@@ -98,12 +98,12 @@ watch(
|
|||||||
<OnClickOutside @trigger="open = false">
|
<OnClickOutside @trigger="open = false">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
color="slate"
|
||||||
:label="selectedLabel"
|
:label="selectedLabel"
|
||||||
icon-position="right"
|
trailing-icon
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
class="justify-between w-full !px-2 !py-2.5 text-n-slate-12 font-normal group-hover/combobox:border-n-slate-6"
|
class="justify-between w-full !px-3 !py-2.5 text-n-slate-12 font-normal group-hover/combobox:border-n-slate-6"
|
||||||
:icon="open ? 'chevron-lucide-up' : 'chevron-lucide-down'"
|
:icon="open ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
|
||||||
icon-lib="lucide"
|
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
@@ -111,12 +111,7 @@ watch(
|
|||||||
class="absolute z-50 w-full mt-1 transition-opacity duration-200 border rounded-md shadow-lg bg-n-solid-1 border-n-strong"
|
class="absolute z-50 w-full mt-1 transition-opacity duration-200 border rounded-md shadow-lg bg-n-solid-1 border-n-strong"
|
||||||
>
|
>
|
||||||
<div class="relative border-b border-n-strong">
|
<div class="relative border-b border-n-strong">
|
||||||
<FluentIcon
|
<span class="absolute i-lucide-search top-2.5 size-4 left-3" />
|
||||||
icon="search"
|
|
||||||
:size="14"
|
|
||||||
class="absolute text-gray-400 dark:text-slate-500 top-3 left-3"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<input
|
<input
|
||||||
ref="searchInput"
|
ref="searchInput"
|
||||||
v-model="search"
|
v-model="search"
|
||||||
@@ -133,7 +128,7 @@ watch(
|
|||||||
<li
|
<li
|
||||||
v-for="option in filteredOptions"
|
v-for="option in filteredOptions"
|
||||||
:key="option.value"
|
:key="option.value"
|
||||||
class="flex items-center justify-between !text-n-slate-12 w-full gap-2 px-2 py-2 text-sm transition-colors duration-150 cursor-pointer hover:bg-n-solid-2"
|
class="flex items-center justify-between !text-n-slate-12 w-full gap-2 px-3 py-2 text-sm transition-colors duration-150 cursor-pointer hover:bg-n-solid-2"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-n-solid-2': option.value === selectedValue,
|
'bg-n-solid-2': option.value === selectedValue,
|
||||||
}"
|
}"
|
||||||
@@ -144,12 +139,9 @@ watch(
|
|||||||
<span :class="{ 'font-medium': option.value === selectedValue }">
|
<span :class="{ 'font-medium': option.value === selectedValue }">
|
||||||
{{ option.label }}
|
{{ option.label }}
|
||||||
</span>
|
</span>
|
||||||
<FluentIcon
|
<span
|
||||||
v-if="option.value === selectedValue"
|
v-if="option.value === selectedValue"
|
||||||
icon="checkmark"
|
class="flex-shrink-0 i-lucide-check size-4 text-n-slate-11"
|
||||||
:size="16"
|
|
||||||
class="flex-shrink-0 text-n-slate-11 dark:text-n-slate-11"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
|||||||
@@ -99,14 +99,15 @@ defineExpose({ open, close });
|
|||||||
<div class="flex items-center justify-between w-full gap-3">
|
<div class="flex items-center justify-between w-full gap-3">
|
||||||
<Button
|
<Button
|
||||||
v-if="showCancelButton"
|
v-if="showCancelButton"
|
||||||
variant="ghost"
|
variant="faded"
|
||||||
|
color="slate"
|
||||||
:label="cancelButtonLabel || t('DIALOG.BUTTONS.CANCEL')"
|
:label="cancelButtonLabel || t('DIALOG.BUTTONS.CANCEL')"
|
||||||
class="w-full bg-n-alpha-2 hover:bg-n-alpha-3"
|
class="w-full"
|
||||||
@click="close"
|
@click="close"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="showConfirmButton"
|
v-if="showConfirmButton"
|
||||||
:variant="type === 'edit' ? 'default' : 'destructive'"
|
:color="type === 'edit' ? 'blue' : 'ruby'"
|
||||||
:label="confirmButtonLabel || t('DIALOG.BUTTONS.CONFIRM')"
|
:label="confirmButtonLabel || t('DIALOG.BUTTONS.CONFIRM')"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
|
|
||||||
import Button from 'dashboard/components-next/button/Button.vue';
|
import Icon from 'dashboard/components-next/icon/Icon.vue';
|
||||||
import Thumbnail from 'dashboard/components-next/thumbnail/Thumbnail.vue';
|
import Thumbnail from 'dashboard/components-next/thumbnail/Thumbnail.vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
@@ -29,29 +29,28 @@ const handleAction = (action, value) => {
|
|||||||
<div
|
<div
|
||||||
class="bg-n-alpha-3 backdrop-blur-[100px] absolute rounded-xl z-50 py-2 px-2 gap-2 flex flex-col min-w-[136px] shadow-lg"
|
class="bg-n-alpha-3 backdrop-blur-[100px] absolute rounded-xl z-50 py-2 px-2 gap-2 flex flex-col min-w-[136px] shadow-lg"
|
||||||
>
|
>
|
||||||
<Button
|
<button
|
||||||
v-for="item in menuItems"
|
v-for="item in menuItems"
|
||||||
:key="item.action"
|
:key="item.action"
|
||||||
:label="item.label"
|
class="inline-flex items-center justify-start w-full h-8 min-w-0 gap-2 px-2 py-1.5 transition-all duration-200 ease-in-out border-0 rounded-lg z-60 hover:bg-n-alpha-1 dark:hover:bg-n-alpha-2 disabled:cursor-not-allowed disabled:pointer-events-none disabled:opacity-50"
|
||||||
:icon="item.icon"
|
:class="{
|
||||||
:emoji="item.emoji"
|
'bg-n-alpha-1 dark:bg-n-solid-active': item.isSelected,
|
||||||
|
'text-n-ruby-11': item.action === 'delete',
|
||||||
|
'text-n-slate-12': item.action !== 'delete',
|
||||||
|
}"
|
||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
class="!justify-start w-full hover:!bg-n-slate-3 dark:hover:!bg-n-slate-4 z-60 px-2 font-normal"
|
|
||||||
:class="item.isSelected ? '!bg-n-alpha-1 dark:!bg-n-solid-active' : ''"
|
|
||||||
:text-variant="item.action === 'delete' ? 'danger' : ''"
|
|
||||||
@click="handleAction(item.action, item.value)"
|
@click="handleAction(item.action, item.value)"
|
||||||
>
|
>
|
||||||
<template #leftPrefix>
|
<Thumbnail
|
||||||
<Thumbnail
|
v-if="item.thumbnail"
|
||||||
v-if="item.thumbnail"
|
:author="item.thumbnail"
|
||||||
:author="item.thumbnail"
|
:name="item.thumbnail.name"
|
||||||
:name="item.thumbnail.name"
|
:size="thumbnailSize"
|
||||||
:size="thumbnailSize"
|
:src="item.thumbnail.src"
|
||||||
:src="item.thumbnail.src"
|
/>
|
||||||
/>
|
<Icon v-if="item.icon" :icon="item.icon" />
|
||||||
</template>
|
<span v-if="item.emoji">{{ item.emoji }}</span>
|
||||||
</Button>
|
<span v-if="item.label" class="text-sm">{{ item.label }}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ const handleInput = event => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative flex flex-col gap-1">
|
<div class="relative flex flex-col min-w-0 gap-1">
|
||||||
<label
|
<label
|
||||||
v-if="label"
|
v-if="label"
|
||||||
:for="id"
|
:for="id"
|
||||||
@@ -92,7 +92,7 @@ const handleInput = event => {
|
|||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
v-if="message"
|
v-if="message"
|
||||||
class="mt-1 mb-0 text-xs truncate transition-all duration-500 ease-in-out"
|
class="min-w-0 mt-1 mb-0 text-xs truncate transition-all duration-500 ease-in-out"
|
||||||
:class="messageClass"
|
:class="messageClass"
|
||||||
>
|
>
|
||||||
{{ message }}
|
{{ message }}
|
||||||
|
|||||||
@@ -65,18 +65,18 @@ const pageInfo = computed(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
icon="chevrons-lucide-left"
|
icon="i-lucide-chevrons-left"
|
||||||
icon-lib="lucide"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="!w-8 !h-6"
|
||||||
:disabled="isFirstPage"
|
:disabled="isFirstPage"
|
||||||
@click="changePage(1)"
|
@click="changePage(1)"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon="chevron-lucide-left"
|
icon="i-lucide-chevron-left"
|
||||||
icon-lib="lucide"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="!w-8 !h-6"
|
||||||
:disabled="isFirstPage"
|
:disabled="isFirstPage"
|
||||||
@click="changePage(currentPage - 1)"
|
@click="changePage(currentPage - 1)"
|
||||||
/>
|
/>
|
||||||
@@ -87,18 +87,18 @@ const pageInfo = computed(() => {
|
|||||||
<span>{{ pageInfo }}</span>
|
<span>{{ pageInfo }}</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
icon="chevron-lucide-right"
|
icon="i-lucide-chevron-right"
|
||||||
icon-lib="lucide"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="!w-8 !h-6"
|
||||||
:disabled="isLastPage"
|
:disabled="isLastPage"
|
||||||
@click="changePage(currentPage + 1)"
|
@click="changePage(currentPage + 1)"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon="chevrons-lucide-right"
|
icon="i-lucide-chevrons-right"
|
||||||
icon-lib="lucide"
|
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
class="!w-8 !h-6"
|
||||||
:disabled="isLastPage"
|
:disabled="isLastPage"
|
||||||
@click="changePage(totalPages)"
|
@click="changePage(totalPages)"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const showDivider = index => {
|
|||||||
class="relative px-4 truncate py-1.5 text-sm border-0 outline-1 outline rounded-lg transition-colors duration-300 ease-in-out hover:text-n-brand"
|
class="relative px-4 truncate py-1.5 text-sm border-0 outline-1 outline rounded-lg transition-colors duration-300 ease-in-out hover:text-n-brand"
|
||||||
:class="[
|
:class="[
|
||||||
activeTab === index
|
activeTab === index
|
||||||
? 'text-n-brand bg-n-solid-active outline-n-container dark:outline-transparent'
|
? 'text-n-blue-text bg-n-solid-active outline-n-container dark:outline-transparent'
|
||||||
: 'text-n-slate-10 outline-transparent h-8',
|
: 'text-n-slate-10 outline-transparent h-8',
|
||||||
]"
|
]"
|
||||||
@click="selectTab(index)"
|
@click="selectTab(index)"
|
||||||
@@ -54,7 +54,7 @@ const showDivider = index => {
|
|||||||
class="w-px h-3.5 rounded my-auto transition-colors duration-300 ease-in-out"
|
class="w-px h-3.5 rounded my-auto transition-colors duration-300 ease-in-out"
|
||||||
:class="
|
:class="
|
||||||
showDivider(index)
|
showDivider(index)
|
||||||
? 'bg-slate-75 dark:bg-slate-800'
|
? 'bg-n-strong'
|
||||||
: 'bg-transparent dark:bg-transparent'
|
: 'bg-transparent dark:bg-transparent'
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import { OnClickOutside } from '@vueuse/components';
|
import { OnClickOutside } from '@vueuse/components';
|
||||||
|
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
import InlineInput from 'dashboard/components-next/inline-input/InlineInput.vue';
|
import InlineInput from 'dashboard/components-next/inline-input/InlineInput.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -71,10 +70,8 @@ watch(
|
|||||||
<span class="flex-grow min-w-0 text-sm truncate text-n-slate-12">
|
<span class="flex-grow min-w-0 text-sm truncate text-n-slate-12">
|
||||||
{{ tag }}
|
{{ tag }}
|
||||||
</span>
|
</span>
|
||||||
<FluentIcon
|
<span
|
||||||
icon="dismiss"
|
class="flex-shrink-0 cursor-pointer i-lucide-x size-3.5 text-n-slate-11"
|
||||||
size="20"
|
|
||||||
class="flex-shrink-0 p-1 cursor-pointer text-n-slate-11"
|
|
||||||
@click.stop="removeTag(index)"
|
@click.stop="removeTag(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import { computed, ref } from 'vue';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { removeEmoji } from 'shared/helpers/emoji';
|
import { removeEmoji } from 'shared/helpers/emoji';
|
||||||
|
|
||||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
author: {
|
author: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -58,7 +56,7 @@ const fontSize = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const iconSize = computed(() => {
|
const iconSize = computed(() => {
|
||||||
return props.size / 2;
|
return Math.round(props.size / 1.8);
|
||||||
});
|
});
|
||||||
|
|
||||||
const shouldShowImage = computed(() => {
|
const shouldShowImage = computed(() => {
|
||||||
@@ -76,7 +74,7 @@ const onImgLoad = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center rounded-full bg-slate-100 dark:bg-slate-700/50"
|
class="flex items-center justify-center rounded-full bg-n-slate-3 dark:bg-n-slate-4"
|
||||||
:style="{ width: `${size}px`, height: `${size}px` }"
|
:style="{ width: `${size}px`, height: `${size}px` }"
|
||||||
>
|
>
|
||||||
<div v-if="author">
|
<div v-if="author">
|
||||||
@@ -91,7 +89,7 @@ const onImgLoad = () => {
|
|||||||
<template v-else>
|
<template v-else>
|
||||||
<span
|
<span
|
||||||
v-if="showAuthorName"
|
v-if="showAuthorName"
|
||||||
class="flex items-center justify-center font-medium text-slate-500 dark:text-slate-400"
|
class="flex items-center justify-center font-medium text-n-slate-11"
|
||||||
:style="{ fontSize: `${fontSize}px` }"
|
:style="{ fontSize: `${fontSize}px` }"
|
||||||
>
|
>
|
||||||
{{ authorInitial }}
|
{{ authorInitial }}
|
||||||
@@ -100,12 +98,10 @@ const onImgLoad = () => {
|
|||||||
v-else
|
v-else
|
||||||
class="flex items-center justify-center w-full h-full rounded-xl"
|
class="flex items-center justify-center w-full h-full rounded-xl"
|
||||||
>
|
>
|
||||||
<FluentIcon
|
<span
|
||||||
v-if="iconName"
|
v-if="iconName"
|
||||||
:icon="iconName"
|
:class="`${iconName} text-n-brand/70`"
|
||||||
icon-lib="lucide"
|
:style="{ width: `${iconSize}px`, height: `${iconSize}px` }"
|
||||||
:size="iconSize"
|
|
||||||
class="text-n-brand"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -113,14 +109,9 @@ const onImgLoad = () => {
|
|||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
v-tooltip.top-start="t('THUMBNAIL.AUTHOR.NOT_AVAILABLE')"
|
v-tooltip.top-start="t('THUMBNAIL.AUTHOR.NOT_AVAILABLE')"
|
||||||
class="flex items-center justify-center w-4 h-4 rounded-full bg-slate-100 dark:bg-slate-700/50"
|
class="flex items-center justify-center w-4 h-4 rounded-full bg-n-slate-3 dark:bg-n-slate-4"
|
||||||
>
|
>
|
||||||
<FluentIcon
|
<span class="i-lucide-user size-2.5 text-n-brand" />
|
||||||
icon="person"
|
|
||||||
type="filled"
|
|
||||||
size="10"
|
|
||||||
class="text-woot-500 dark:text-woot-400"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const getArticleStatus = status => {
|
|||||||
export const HELP_CENTER_MENU_ITEMS = [
|
export const HELP_CENTER_MENU_ITEMS = [
|
||||||
{
|
{
|
||||||
label: 'Articles',
|
label: 'Articles',
|
||||||
icon: 'book',
|
icon: 'i-lucide-book',
|
||||||
action: 'portals_articles_index',
|
action: 'portals_articles_index',
|
||||||
value: [
|
value: [
|
||||||
'portals_articles_index',
|
'portals_articles_index',
|
||||||
@@ -41,7 +41,7 @@ export const HELP_CENTER_MENU_ITEMS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Categories',
|
label: 'Categories',
|
||||||
icon: 'folder',
|
icon: 'i-lucide-folder',
|
||||||
action: 'portals_categories_index',
|
action: 'portals_categories_index',
|
||||||
value: [
|
value: [
|
||||||
'portals_categories_index',
|
'portals_categories_index',
|
||||||
@@ -51,13 +51,13 @@ export const HELP_CENTER_MENU_ITEMS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Locales',
|
label: 'Locales',
|
||||||
icon: 'translate',
|
icon: 'i-lucide-languages',
|
||||||
action: 'portals_locales_index',
|
action: 'portals_locales_index',
|
||||||
value: ['portals_locales_index'],
|
value: ['portals_locales_index'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
icon: 'settings',
|
icon: 'i-lucide-settings',
|
||||||
action: 'portals_settings_index',
|
action: 'portals_settings_index',
|
||||||
value: ['portals_settings_index'],
|
value: ['portals_settings_index'],
|
||||||
},
|
},
|
||||||
@@ -74,25 +74,25 @@ export const ARTICLE_MENU_ITEMS = {
|
|||||||
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.PUBLISH',
|
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.PUBLISH',
|
||||||
value: ARTICLE_STATUSES.PUBLISHED,
|
value: ARTICLE_STATUSES.PUBLISHED,
|
||||||
action: 'publish',
|
action: 'publish',
|
||||||
icon: 'checkmark',
|
icon: 'i-lucide-check',
|
||||||
},
|
},
|
||||||
draft: {
|
draft: {
|
||||||
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.DRAFT',
|
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.DRAFT',
|
||||||
value: ARTICLE_STATUSES.DRAFT,
|
value: ARTICLE_STATUSES.DRAFT,
|
||||||
action: 'draft',
|
action: 'draft',
|
||||||
icon: 'draft',
|
icon: 'i-lucide-pencil-line',
|
||||||
},
|
},
|
||||||
archive: {
|
archive: {
|
||||||
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.ARCHIVE',
|
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.ARCHIVE',
|
||||||
value: ARTICLE_STATUSES.ARCHIVED,
|
value: ARTICLE_STATUSES.ARCHIVED,
|
||||||
action: 'archive',
|
action: 'archive',
|
||||||
icon: 'archive',
|
icon: 'i-lucide-archive-restore',
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.DELETE',
|
label: 'HELP_CENTER.ARTICLES_PAGE.ARTICLE_CARD.CARD.DROPDOWN_MENU.DELETE',
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
icon: 'delete',
|
icon: 'i-lucide-trash',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -135,13 +135,13 @@ export const LOCALE_MENU_ITEMS = [
|
|||||||
label: 'HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.MAKE_DEFAULT',
|
label: 'HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.MAKE_DEFAULT',
|
||||||
action: 'change-default',
|
action: 'change-default',
|
||||||
value: 'default',
|
value: 'default',
|
||||||
icon: 'star-emphasis',
|
icon: 'i-lucide-star',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.DELETE',
|
label: 'HELP_CENTER.LOCALES_PAGE.LOCALE_CARD.DROPDOWN_MENU.DELETE',
|
||||||
action: 'delete',
|
action: 'delete',
|
||||||
value: 'delete',
|
value: 'delete',
|
||||||
icon: 'delete',
|
icon: 'i-lucide-trash',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@
|
|||||||
"@iconify-json/ri": "^1.2.1",
|
"@iconify-json/ri": "^1.2.1",
|
||||||
"@histoire/plugin-vue": "0.17.15",
|
"@histoire/plugin-vue": "0.17.15",
|
||||||
"@iconify-json/logos": "^1.2.0",
|
"@iconify-json/logos": "^1.2.0",
|
||||||
"@iconify-json/lucide": "^1.2.5",
|
"@iconify-json/lucide": "^1.2.10",
|
||||||
"@size-limit/file": "^8.2.4",
|
"@size-limit/file": "^8.2.4",
|
||||||
"@vitest/coverage-v8": "2.0.1",
|
"@vitest/coverage-v8": "2.0.1",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
@@ -142,6 +142,13 @@
|
|||||||
"pre-push": "sh bin/validate_push"
|
"pre-push": "sh bin/validate_push"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"vite-node": "2.0.1",
|
||||||
|
"vite": "5.4.8",
|
||||||
|
"vitest": "2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"app/**/*.{js,vue}": [
|
"app/**/*.{js,vue}": [
|
||||||
"eslint --fix",
|
"eslint --fix",
|
||||||
|
|||||||
51
pnpm-lock.yaml
generated
51
pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
|||||||
autoInstallPeers: true
|
autoInstallPeers: true
|
||||||
excludeLinksFromLockfile: false
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
vite-node: 2.0.1
|
||||||
|
vite: 5.4.8
|
||||||
|
vitest: 2.0.1
|
||||||
|
|
||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
@@ -223,8 +228,8 @@ importers:
|
|||||||
specifier: ^1.2.0
|
specifier: ^1.2.0
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
'@iconify-json/lucide':
|
'@iconify-json/lucide':
|
||||||
specifier: ^1.2.5
|
specifier: ^1.2.10
|
||||||
version: 1.2.5
|
version: 1.2.10
|
||||||
'@iconify-json/ri':
|
'@iconify-json/ri':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
@@ -301,7 +306,7 @@ importers:
|
|||||||
specifier: ^3.4.13
|
specifier: ^3.4.13
|
||||||
version: 3.4.13
|
version: 3.4.13
|
||||||
vite:
|
vite:
|
||||||
specifier: ^5.4.8
|
specifier: 5.4.8
|
||||||
version: 5.4.8(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
version: 5.4.8(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
||||||
vite-plugin-ruby:
|
vite-plugin-ruby:
|
||||||
specifier: ^5.0.0
|
specifier: ^5.0.0
|
||||||
@@ -817,7 +822,7 @@ packages:
|
|||||||
'@histoire/shared@0.17.17':
|
'@histoire/shared@0.17.17':
|
||||||
resolution: {integrity: sha512-ueGtURysonT0MujCObPCR57+mgZluMEXCrbc2FBgKAD/DoAt38tNwSGsmLldk2O6nTr7lr6ClbVSgWrLwgY6Xw==}
|
resolution: {integrity: sha512-ueGtURysonT0MujCObPCR57+mgZluMEXCrbc2FBgKAD/DoAt38tNwSGsmLldk2O6nTr7lr6ClbVSgWrLwgY6Xw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.9.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
|
vite: 5.4.8
|
||||||
|
|
||||||
'@histoire/vendors@0.17.17':
|
'@histoire/vendors@0.17.17':
|
||||||
resolution: {integrity: sha512-QZvmffdoJlLuYftPIkOU5Q2FPAdG2JjMuQ5jF7NmEl0n1XnmbMqtRkdYTZ4eF6CO1KLZ0Zyf6gBQvoT1uWNcjA==}
|
resolution: {integrity: sha512-QZvmffdoJlLuYftPIkOU5Q2FPAdG2JjMuQ5jF7NmEl0n1XnmbMqtRkdYTZ4eF6CO1KLZ0Zyf6gBQvoT1uWNcjA==}
|
||||||
@@ -838,8 +843,8 @@ packages:
|
|||||||
'@iconify-json/logos@1.2.0':
|
'@iconify-json/logos@1.2.0':
|
||||||
resolution: {integrity: sha512-VkU9QSqeZR2guWbecdqkcoZEAJfgJJTUm6QAsypuZQ7Cve6zy39wOXDjp2H31I8QyQy4O3Cz96+pNji6UQFg4w==}
|
resolution: {integrity: sha512-VkU9QSqeZR2guWbecdqkcoZEAJfgJJTUm6QAsypuZQ7Cve6zy39wOXDjp2H31I8QyQy4O3Cz96+pNji6UQFg4w==}
|
||||||
|
|
||||||
'@iconify-json/lucide@1.2.5':
|
'@iconify-json/lucide@1.2.10':
|
||||||
resolution: {integrity: sha512-ZRw1GRcN5CQ+9BW+yBEFjRNf4pQfsU8gvNVcCY81yVmwKLUegTncGHXjBuspK6HSmsJspOhGBgLqNbHb0dpxfw==}
|
resolution: {integrity: sha512-cR1xpRJ4dnoXlC0ShDjzbrZyu+ICH4OUaYl7S51MhZUO1H040s7asVqv0LsDbofSLDuzWkHCLsBabTTRL0mCUg==}
|
||||||
|
|
||||||
'@iconify-json/ri@1.2.1':
|
'@iconify-json/ri@1.2.1':
|
||||||
resolution: {integrity: sha512-xI3+xZHBI+wlhQqd6jRRcLD5K8B8vQNyxcSB43myxNZ/SfXIn7Ny28h0jyPo9e0gT8fGhqx6R5PeLz/UBB8jwQ==}
|
resolution: {integrity: sha512-xI3+xZHBI+wlhQqd6jRRcLD5K8B8vQNyxcSB43myxNZ/SfXIn7Ny28h0jyPo9e0gT8fGhqx6R5PeLz/UBB8jwQ==}
|
||||||
@@ -1713,7 +1718,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
|
resolution: {integrity: sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^5.0.0
|
vite: 5.4.8
|
||||||
vue: ^3.2.25
|
vue: ^3.2.25
|
||||||
|
|
||||||
'@vitest/coverage-v8@2.0.1':
|
'@vitest/coverage-v8@2.0.1':
|
||||||
@@ -2907,7 +2912,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-DiRMSIgj340z+zikqf0f3Pj0CTv2/xtdBMBIAO1EARat+QXxMwumbfK41Gi7f9IIBr+UVmomNcwFxVY2EM/vrw==}
|
resolution: {integrity: sha512-DiRMSIgj340z+zikqf0f3Pj0CTv2/xtdBMBIAO1EARat+QXxMwumbfK41Gi7f9IIBr+UVmomNcwFxVY2EM/vrw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: ^2.9.0 || ^3.0.0 || ^4.0.0 || ^5.0.0
|
vite: 5.4.8
|
||||||
|
|
||||||
hotkeys-js@3.8.7:
|
hotkeys-js@3.8.7:
|
||||||
resolution: {integrity: sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==}
|
resolution: {integrity: sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg==}
|
||||||
@@ -4632,11 +4637,6 @@ packages:
|
|||||||
videojs-wavesurfer@3.8.0:
|
videojs-wavesurfer@3.8.0:
|
||||||
resolution: {integrity: sha512-qHucCBiEW+4dZ0Zp1k4R1elprUOV+QDw87UDA9QRXtO7GK/MrSdoe/TMFxP9SLnJCiX9xnYdf4OQgrmvJ9UVVw==}
|
resolution: {integrity: sha512-qHucCBiEW+4dZ0Zp1k4R1elprUOV+QDw87UDA9QRXtO7GK/MrSdoe/TMFxP9SLnJCiX9xnYdf4OQgrmvJ9UVVw==}
|
||||||
|
|
||||||
vite-node@0.34.7:
|
|
||||||
resolution: {integrity: sha512-0Yzb96QzHmqIKIs/x2q/sqG750V/EF6yDkS2p1WjJc1W2bgRSuQjf5vB9HY8h2nVb5j4pO5paS5Npcv3s69YUg==}
|
|
||||||
engines: {node: '>=v14.18.0'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
vite-node@2.0.1:
|
vite-node@2.0.1:
|
||||||
resolution: {integrity: sha512-nVd6kyhPAql0s+xIVJzuF+RSRH8ZimNrm6U8ZvTA4MXv8CHI17TFaQwRaFiK75YX6XeFqZD4IoAaAfi9OR1XvQ==}
|
resolution: {integrity: sha512-nVd6kyhPAql0s+xIVJzuF+RSRH8ZimNrm6U8ZvTA4MXv8CHI17TFaQwRaFiK75YX6XeFqZD4IoAaAfi9OR1XvQ==}
|
||||||
engines: {node: ^18.0.0 || >=20.0.0}
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
@@ -4645,7 +4645,7 @@ packages:
|
|||||||
vite-plugin-ruby@5.0.0:
|
vite-plugin-ruby@5.0.0:
|
||||||
resolution: {integrity: sha512-c8PjTp21Ah/ttgnNUyu0qvCXZI08Jr9I24oUKg3TRIRhF5GcOZ++6wtlTCrNFd9COEQbpXHxlRIXd/MEg0iZJw==}
|
resolution: {integrity: sha512-c8PjTp21Ah/ttgnNUyu0qvCXZI08Jr9I24oUKg3TRIRhF5GcOZ++6wtlTCrNFd9COEQbpXHxlRIXd/MEg0iZJw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: '>=5.0.0'
|
vite: 5.4.8
|
||||||
|
|
||||||
vite@5.4.8:
|
vite@5.4.8:
|
||||||
resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
|
resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
|
||||||
@@ -5496,7 +5496,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/types': 2.0.0
|
'@iconify/types': 2.0.0
|
||||||
|
|
||||||
'@iconify-json/lucide@1.2.5':
|
'@iconify-json/lucide@1.2.10':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@iconify/types': 2.0.0
|
'@iconify/types': 2.0.0
|
||||||
|
|
||||||
@@ -7977,7 +7977,7 @@ snapshots:
|
|||||||
shiki-es: 0.2.0
|
shiki-es: 0.2.0
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
vite: 5.4.8(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
vite: 5.4.8(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
||||||
vite-node: 0.34.7(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
vite-node: 2.0.1(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -9894,25 +9894,6 @@ snapshots:
|
|||||||
video.js: 7.18.1
|
video.js: 7.18.1
|
||||||
wavesurfer.js: 7.8.6
|
wavesurfer.js: 7.8.6
|
||||||
|
|
||||||
vite-node@0.34.7(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0):
|
|
||||||
dependencies:
|
|
||||||
cac: 6.7.14
|
|
||||||
debug: 4.3.7
|
|
||||||
mlly: 1.7.1
|
|
||||||
pathe: 1.1.2
|
|
||||||
picocolors: 1.1.0
|
|
||||||
vite: 5.4.8(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- '@types/node'
|
|
||||||
- less
|
|
||||||
- lightningcss
|
|
||||||
- sass
|
|
||||||
- sass-embedded
|
|
||||||
- stylus
|
|
||||||
- sugarss
|
|
||||||
- supports-color
|
|
||||||
- terser
|
|
||||||
|
|
||||||
vite-node@2.0.1(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0):
|
vite-node@2.0.1(@types/node@22.7.0)(sass@1.79.3)(terser@5.33.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
cac: 6.7.14
|
cac: 6.7.14
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const tailwindConfig = {
|
|||||||
'./app/javascript/portal/**/*.vue',
|
'./app/javascript/portal/**/*.vue',
|
||||||
'./app/javascript/shared/**/*.vue',
|
'./app/javascript/shared/**/*.vue',
|
||||||
'./app/javascript/survey/**/*.vue',
|
'./app/javascript/survey/**/*.vue',
|
||||||
|
'./app/javascript/dashboard/helper/**/*.js',
|
||||||
'./app/views/**/*.html.erb',
|
'./app/views/**/*.html.erb',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
|
|||||||
Reference in New Issue
Block a user