mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
feat: Support bigger font size in dashboard (#10974)
# Pull Request Template ## Description Fixes https://linear.app/chatwoot/issue/CW-4091/accessibility-improvement-support-bigger-font-size-for-the-dashboard ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### **Loom video** https://www.loom.com/share/1ab781859fa748a5ad54aacbacd127b4?sid=a7dd9164-a6de-462f-bff7-1b25e9c55b4f ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
This commit is contained in:
@@ -14,6 +14,7 @@ import WootSnackbarBox from './components/SnackbarContainer.vue';
|
||||
import { setColorTheme } from './helper/themeHelper';
|
||||
import { isOnOnboardingView } from 'v3/helpers/RouteHelper';
|
||||
import { useAccount } from 'dashboard/composables/useAccount';
|
||||
import { useFontSize } from 'dashboard/composables/useFontSize';
|
||||
import {
|
||||
registerSubscription,
|
||||
verifyServiceWorkerExistence,
|
||||
@@ -37,8 +38,15 @@ export default {
|
||||
const router = useRouter();
|
||||
const store = useStore();
|
||||
const { accountId } = useAccount();
|
||||
// Use the font size composable (it automatically sets up the watcher)
|
||||
const { currentFontSize } = useFontSize();
|
||||
|
||||
return { router, store, currentAccountId: accountId };
|
||||
return {
|
||||
router,
|
||||
store,
|
||||
currentAccountId: accountId,
|
||||
currentFontSize,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
@@ -82,7 +82,7 @@ input[type='url']:not(.reset-base) {
|
||||
}
|
||||
|
||||
input[type='file'] {
|
||||
@apply bg-white dark:bg-slate-800 leading-[1.15] mb-4;
|
||||
@apply bg-white dark:bg-n-solid-1 leading-[1.15] mb-4;
|
||||
}
|
||||
|
||||
// Select
|
||||
@@ -141,11 +141,16 @@ code {
|
||||
@apply text-xs border-0;
|
||||
|
||||
&.hljs {
|
||||
@apply bg-slate-50 dark:bg-slate-700 text-slate-800 dark:text-slate-50 rounded-lg p-5;
|
||||
@apply bg-n-slate-3 dark:bg-n-solid-3 text-slate-800 dark:text-slate-50 rounded-lg p-5;
|
||||
|
||||
.hljs-number,
|
||||
.hljs-string {
|
||||
@apply text-red-800 dark:text-red-400;
|
||||
}
|
||||
|
||||
.hljs-name,
|
||||
.hljs-tag {
|
||||
@apply text-n-slate-11;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const handleButtonClick = () => {
|
||||
<template>
|
||||
<section class="flex flex-col w-full h-full overflow-hidden bg-n-background">
|
||||
<header class="sticky top-0 z-10 px-6 lg:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto">
|
||||
<div class="w-full max-w-[60rem] mx-auto">
|
||||
<div class="flex items-center justify-between w-full h-20 gap-2">
|
||||
<span class="text-xl font-medium text-n-slate-12">
|
||||
{{ headerTitle }}
|
||||
@@ -44,7 +44,7 @@ const handleButtonClick = () => {
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 px-6 overflow-y-auto lg:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto py-4">
|
||||
<div class="w-full max-w-[60rem] mx-auto py-4">
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -40,7 +40,7 @@ const handleSubmit = campaignDetails => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-[400px] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6 max-h-[85vh] overflow-y-auto"
|
||||
class="w-[25rem] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6 max-h-[85vh] overflow-y-auto"
|
||||
>
|
||||
<h3 class="text-base font-medium text-slate-900 dark:text-slate-50">
|
||||
{{ t(`CAMPAIGN.LIVE_CHAT.CREATE.TITLE`) }}
|
||||
|
||||
@@ -39,7 +39,7 @@ const handleClose = () => emit('close');
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-[400px] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6"
|
||||
class="w-[25rem] z-50 min-w-0 absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6"
|
||||
>
|
||||
<h3 class="text-base font-medium text-slate-900 dark:text-slate-50">
|
||||
{{ t(`CAMPAIGN.SMS.CREATE.TITLE`) }}
|
||||
|
||||
@@ -65,8 +65,8 @@ const toggleBlock = () => {
|
||||
<div
|
||||
class="flex flex-col w-full h-full transition-all duration-300 ltr:2xl:ml-56 rtl:2xl:mr-56"
|
||||
>
|
||||
<header class="sticky top-0 z-10 px-6 xl:px-0">
|
||||
<div class="w-full mx-auto max-w-[650px]">
|
||||
<header class="sticky top-0 z-10 px-6 3xl:px-0">
|
||||
<div class="w-full mx-auto max-w-[40.625rem]">
|
||||
<div class="flex items-center justify-between w-full h-20 gap-2">
|
||||
<Breadcrumb
|
||||
:items="breadcrumbItems"
|
||||
@@ -98,8 +98,8 @@ const toggleBlock = () => {
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 px-6 overflow-y-auto xl:px-px">
|
||||
<div class="w-full py-4 mx-auto max-w-[650px]">
|
||||
<main class="flex-1 px-6 overflow-y-auto 3xl:px-px">
|
||||
<div class="w-full py-4 mx-auto max-w-[40.625rem]">
|
||||
<slot name="default" />
|
||||
</div>
|
||||
</main>
|
||||
@@ -107,7 +107,7 @@ const toggleBlock = () => {
|
||||
|
||||
<div
|
||||
v-if="slots.sidebar"
|
||||
class="overflow-y-auto justify-end min-w-[200px] w-full py-6 max-w-[440px] border-l border-n-weak bg-n-solid-2"
|
||||
class="overflow-y-auto justify-end min-w-52 w-full py-6 max-w-md border-l border-n-weak bg-n-solid-2"
|
||||
>
|
||||
<slot name="sidebar" />
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@ const emit = defineEmits([
|
||||
<template>
|
||||
<header class="sticky top-0 z-10">
|
||||
<div
|
||||
class="flex items-center justify-between w-full h-20 px-6 gap-2 mx-auto max-w-[960px]"
|
||||
class="flex items-center justify-between w-full h-20 px-6 gap-2 mx-auto max-w-[60rem]"
|
||||
>
|
||||
<span class="text-xl font-medium truncate text-n-slate-12">
|
||||
{{ headerTitle }}
|
||||
|
||||
@@ -62,7 +62,7 @@ const activeFilterQueryData = computed(() => {
|
||||
t('CONTACTS_LAYOUT.FILTER.ACTIVE_FILTERS.CLEAR_FILTERS')
|
||||
"
|
||||
:show-clear-button="!hasActiveSegments"
|
||||
class="max-w-[960px] px-6"
|
||||
class="max-w-[60rem] px-6"
|
||||
@open-filter="emit('openFilter')"
|
||||
@clear-filters="emit('clearFilters')"
|
||||
/>
|
||||
|
||||
@@ -72,7 +72,7 @@ const openFilter = () => {
|
||||
@clear-filters="emit('clearFilters')"
|
||||
/>
|
||||
<main class="flex-1 overflow-y-auto">
|
||||
<div class="w-full mx-auto max-w-[960px]">
|
||||
<div class="w-full mx-auto max-w-[60rem]">
|
||||
<ContactsActiveFiltersPreview
|
||||
v-if="
|
||||
(hasAppliedFilters || !isNotSegmentView) &&
|
||||
|
||||
@@ -22,7 +22,7 @@ defineProps({
|
||||
class="relative flex flex-col items-center justify-center w-full h-full overflow-hidden"
|
||||
>
|
||||
<div
|
||||
class="relative w-full max-w-[960px] mx-auto overflow-hidden h-full max-h-[448px]"
|
||||
class="relative w-full max-w-[60rem] mx-auto overflow-hidden h-full max-h-[28rem]"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full space-y-4 overflow-y-hidden opacity-50 pointer-events-none"
|
||||
|
||||
@@ -60,7 +60,7 @@ const togglePortalSwitcher = () => {
|
||||
<template>
|
||||
<section class="flex flex-col w-full h-full overflow-hidden bg-n-background">
|
||||
<header class="sticky top-0 z-10 px-6 pb-3 lg:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto lg:px-6">
|
||||
<div class="w-full max-w-[60rem] mx-auto lg:px-6">
|
||||
<div
|
||||
v-if="showHeaderTitle"
|
||||
class="flex items-center justify-start h-20 gap-2"
|
||||
@@ -96,7 +96,7 @@ const togglePortalSwitcher = () => {
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 px-6 overflow-y-auto lg:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto py-3 lg:px-6">
|
||||
<div class="w-full max-w-[60rem] mx-auto py-3 lg:px-6">
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -52,7 +52,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col absolute w-[400px] bg-n-alpha-3 outline outline-1 outline-n-container backdrop-blur-[100px] shadow-lg gap-6 rounded-xl p-6"
|
||||
class="flex flex-col absolute w-[25rem] bg-n-alpha-3 outline outline-1 outline-n-container backdrop-blur-[100px] shadow-lg gap-6 rounded-xl p-6"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<h3>
|
||||
@@ -75,7 +75,7 @@ onMounted(() => {
|
||||
<div>
|
||||
<div class="flex justify-between w-full gap-4 py-2">
|
||||
<label
|
||||
class="text-sm font-medium whitespace-nowrap min-w-[100px] text-slate-900 dark:text-slate-50"
|
||||
class="text-sm font-medium whitespace-nowrap min-w-[6.25rem] text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
{{
|
||||
t(
|
||||
@@ -90,9 +90,9 @@ onMounted(() => {
|
||||
'HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_DESCRIPTION_PLACEHOLDER'
|
||||
)
|
||||
"
|
||||
class="w-[220px]"
|
||||
class="w-[13.75rem]"
|
||||
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-[9.375rem]"
|
||||
auto-height
|
||||
min-height="3rem"
|
||||
/>
|
||||
@@ -108,12 +108,12 @@ onMounted(() => {
|
||||
:label="
|
||||
t('HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_TITLE')
|
||||
"
|
||||
custom-label-class="min-w-[120px]"
|
||||
custom-label-class="min-w-[7.5rem]"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between w-full gap-3 py-2">
|
||||
<label
|
||||
class="text-sm font-medium whitespace-nowrap min-w-[120px] text-slate-900 dark:text-slate-50"
|
||||
class="text-sm font-medium whitespace-nowrap min-w-[7.5rem] text-slate-900 dark:text-slate-50"
|
||||
>
|
||||
{{
|
||||
t('HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_TAGS')
|
||||
@@ -126,7 +126,7 @@ onMounted(() => {
|
||||
'HELP_CENTER.EDIT_ARTICLE_PAGE.ARTICLE_PROPERTIES.META_TAGS_PLACEHOLDER'
|
||||
)
|
||||
"
|
||||
class="w-[224px]"
|
||||
class="w-[14rem]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -90,7 +90,7 @@ const handleCategory = async formData => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-[400px] absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6"
|
||||
class="w-[25rem] absolute top-10 ltr:right-0 rtl:left-0 bg-n-alpha-3 backdrop-blur-[100px] p-6 rounded-xl border border-slate-50 dark:border-slate-900 shadow-md flex flex-col gap-6"
|
||||
>
|
||||
<h3 class="text-base font-medium text-slate-900 dark:text-slate-50">
|
||||
{{
|
||||
|
||||
@@ -201,7 +201,7 @@ defineExpose({ state, isSubmitDisabled });
|
||||
color="slate"
|
||||
size="sm"
|
||||
:icon="!state.icon ? 'i-lucide-smile-plus' : ''"
|
||||
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"
|
||||
class="!h-[2.4rem] !w-[2.375rem] absolute top-[1.94rem] !outline-none !rounded-[0.438rem] border-0 ltr:left-px rtl:right-px ltr:!rounded-r-none rtl:!rounded-l-none"
|
||||
@click="isEmojiPickerOpen = !isEmojiPickerOpen"
|
||||
/>
|
||||
<EmojiInput
|
||||
|
||||
@@ -74,7 +74,7 @@ const handleDeletePortal = () => {
|
||||
</div>
|
||||
<div
|
||||
v-else-if="activePortal"
|
||||
class="flex flex-col w-full gap-4 max-w-[640px] pb-8"
|
||||
class="flex flex-col w-full gap-4 max-w-[40rem] pb-8"
|
||||
>
|
||||
<PortalBaseSettings
|
||||
:active-portal="activePortal"
|
||||
|
||||
@@ -92,7 +92,7 @@ const redirectToPortalHomePage = () => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="pt-5 pb-3 bg-n-alpha-3 backdrop-blur-[100px] outline outline-n-container outline-1 z-50 absolute w-[440px] rounded-xl shadow-md flex flex-col gap-4"
|
||||
class="pt-5 pb-3 bg-n-alpha-3 backdrop-blur-[100px] outline outline-n-container outline-1 z-50 absolute w-[27.5rem] rounded-xl shadow-md flex flex-col gap-4"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between gap-4 px-6 pb-3 border-b border-n-alpha-2"
|
||||
|
||||
@@ -148,7 +148,7 @@ useKeyboardEvents(keyboardEvents);
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex items-center justify-between w-full h-[52px] gap-2 px-4 py-3"
|
||||
class="flex items-center justify-between w-full h-[3.25rem] gap-2 px-4 py-3"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<WhatsAppOptions
|
||||
|
||||
@@ -47,10 +47,10 @@ const removeAttachment = id => {
|
||||
<div
|
||||
v-for="attachment in filteredImageAttachments"
|
||||
:key="attachment.id"
|
||||
class="relative group/image w-[72px] h-[72px]"
|
||||
class="relative group/image w-[4.5rem] h-[4.5rem]"
|
||||
>
|
||||
<img
|
||||
class="object-cover w-[72px] h-[72px] rounded-lg"
|
||||
class="object-cover w-[4.5rem] h-[4.5rem] rounded-lg"
|
||||
:src="attachment.thumb"
|
||||
/>
|
||||
<Button
|
||||
@@ -69,7 +69,7 @@ const removeAttachment = id => {
|
||||
<div
|
||||
v-for="attachment in filteredNonImageAttachments"
|
||||
:key="attachment.id"
|
||||
class="max-w-[300px] inline-flex items-center h-8 min-w-0 bg-n-alpha-2 dark:bg-n-solid-3 rounded-lg gap-3 ltr:pl-3 rtl:pr-3 ltr:pr-2 rtl:pl-2"
|
||||
class="max-w-[18.75rem] inline-flex items-center h-8 min-w-0 bg-n-alpha-2 dark:bg-n-solid-3 rounded-lg gap-3 ltr:pl-3 rtl:pr-3 ltr:pr-2 rtl:pl-2"
|
||||
>
|
||||
<span class="text-sm font-medium text-n-slate-11">
|
||||
{{ fileNameWithEllipsis(attachment.resource) }}
|
||||
|
||||
@@ -265,7 +265,7 @@ const handleSendWhatsappMessage = async ({ message, templateParams }) => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-[670px] mt-2 divide-y divide-n-strong overflow-visible transition-all duration-300 ease-in-out top-full justify-between flex flex-col bg-n-alpha-3 border border-n-strong shadow-sm backdrop-blur-[100px] rounded-xl"
|
||||
class="w-[42rem] mt-2 divide-y divide-n-strong overflow-visible transition-all duration-300 ease-in-out top-full justify-between flex flex-col bg-n-alpha-3 border border-n-strong shadow-sm backdrop-blur-[100px] rounded-xl"
|
||||
>
|
||||
<ContactSelector
|
||||
:contacts="contacts"
|
||||
|
||||
@@ -68,7 +68,7 @@ const handlePageChange = event => {
|
||||
<template>
|
||||
<section class="flex flex-col w-full h-full overflow-hidden bg-n-background">
|
||||
<header class="sticky top-0 z-10 px-6 xl:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto">
|
||||
<div class="w-full max-w-[60rem] mx-auto">
|
||||
<div
|
||||
class="flex items-start lg:items-center justify-between w-full py-6 lg:py-0 lg:h-20 gap-4 lg:gap-2 flex-col lg:flex-row"
|
||||
>
|
||||
@@ -96,7 +96,7 @@ const handlePageChange = event => {
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 px-6 overflow-y-auto xl:px-0">
|
||||
<div class="w-full max-w-[960px] mx-auto py-4">
|
||||
<div class="w-full max-w-[60rem] mx-auto py-4">
|
||||
<slot v-if="!showPaywall" name="controls" />
|
||||
<div
|
||||
v-if="isFetching"
|
||||
|
||||
@@ -27,7 +27,7 @@ const openBilling = () => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-full max-w-[960px] mx-auto h-full max-h-[448px] grid place-content-center"
|
||||
class="w-full max-w-[60rem] mx-auto h-full max-h-[448px] grid place-content-center"
|
||||
>
|
||||
<BasePaywallModal
|
||||
class="mx-auto"
|
||||
|
||||
@@ -63,7 +63,7 @@ const pageInfo = computed(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex justify-between h-12 w-full max-w-[957px] outline outline-n-container outline-1 -outline-offset-1 mx-auto bg-n-solid-2 rounded-xl py-2 ltr:pl-4 rtl:pr-4 ltr:pr-3 rtl:pl-3 items-center before:absolute before:inset-x-0 before:-top-4 before:bg-gradient-to-t before:from-n-background before:from-10% before:dark:from-0% before:to-transparent before:h-4 before:pointer-events-none"
|
||||
class="flex justify-between h-12 w-full max-w-[calc(60rem-3px)] outline outline-n-container outline-1 -outline-offset-1 mx-auto bg-n-solid-2 rounded-xl py-2 ltr:pl-4 rtl:pr-4 ltr:pr-3 rtl:pl-3 items-center before:absolute before:inset-x-0 before:-top-4 before:bg-gradient-to-t before:from-n-background before:from-10% before:dark:from-0% before:to-transparent before:h-4 before:pointer-events-none"
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="min-w-0 text-sm font-normal line-clamp-1 text-n-slate-11">
|
||||
|
||||
@@ -185,7 +185,7 @@ watch(
|
||||
"
|
||||
trailing-icon
|
||||
:disabled="disabled"
|
||||
class="!h-[30px] top-1 !px-2 outline-0 !outline-none !rounded-lg border-0 ltr:!rounded-r-none rtl:!rounded-l-none"
|
||||
class="!h-[1.875rem] top-1 !px-2 outline-0 !outline-none !rounded-lg border-0 ltr:!rounded-r-none rtl:!rounded-l-none"
|
||||
@click="toggleCountryDropdown"
|
||||
>
|
||||
<span
|
||||
|
||||
@@ -515,7 +515,7 @@ const menuItems = computed(() => {
|
||||
|
||||
<template>
|
||||
<aside
|
||||
class="w-[200px] bg-n-solid-2 rtl:border-l ltr:border-r border-n-weak h-screen flex flex-col text-sm pb-1"
|
||||
class="w-[12.5rem] bg-n-solid-2 rtl:border-l ltr:border-r border-n-weak h-screen flex flex-col text-sm pb-1"
|
||||
>
|
||||
<section class="grid gap-2 mt-2 mb-4">
|
||||
<div class="flex items-center min-w-0 gap-2 px-2">
|
||||
|
||||
@@ -31,7 +31,7 @@ const shouldRenderComponent = computed(() => {
|
||||
:is="to ? 'router-link' : 'div'"
|
||||
:to="to"
|
||||
:title="label"
|
||||
class="flex h-8 items-center gap-2 px-2 py-1 rounded-lg max-w-[151px] hover:bg-gradient-to-r from-transparent via-n-slate-3/70 to-n-slate-3/70 group"
|
||||
class="flex h-8 items-center gap-2 px-2 py-1 rounded-lg max-w-[9.438rem] hover:bg-gradient-to-r from-transparent via-n-slate-3/70 to-n-slate-3/70 group"
|
||||
:class="{
|
||||
'n-blue-text bg-n-alpha-2 active': active,
|
||||
}"
|
||||
|
||||
@@ -27,7 +27,7 @@ const updateValue = () => {
|
||||
>
|
||||
<span class="sr-only">{{ t('SWITCH.TOGGLE') }}</span>
|
||||
<span
|
||||
class="absolute top-px left-0.5 h-3 w-3 transform rounded-full shadow-sm transition-transform duration-200 ease-in-out"
|
||||
class="absolute top-[0.07rem] left-0.5 h-3 w-3 transform rounded-full shadow-sm transition-transform duration-200 ease-in-out"
|
||||
:class="
|
||||
modelValue
|
||||
? 'translate-x-2.5 bg-white'
|
||||
|
||||
@@ -23,33 +23,34 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="ml-0 mr-0 flex py-8 w-full xl:w-3/4 flex-col xl:flex-row"
|
||||
class="ml-0 mr-0 py-8 w-full"
|
||||
:class="{
|
||||
'border-b border-solid border-slate-50 dark:border-slate-700/30':
|
||||
showBorder,
|
||||
'border-b border-solid border-n-weak/60 dark:border-n-weak': showBorder,
|
||||
}"
|
||||
>
|
||||
<div class="w-full xl:w-1/4 min-w-0 xl:max-w-[30%] pr-12">
|
||||
<p
|
||||
v-if="title"
|
||||
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p
|
||||
class="text-sm mb-2 text-slate-700 dark:text-slate-300 leading-5 tracking-normal mt-2"
|
||||
>
|
||||
<slot v-if="subTitle" name="subTitle">
|
||||
{{ subTitle }}
|
||||
</slot>
|
||||
</p>
|
||||
<p v-if="note">
|
||||
<span class="font-semibold">{{ $t('INBOX_MGMT.NOTE') }}</span>
|
||||
{{ note }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full xl:w-1/2 min-w-0 xl:max-w-[50%]">
|
||||
<slot />
|
||||
<div class="grid grid-cols-1 lg:grid-cols-6 gap-6">
|
||||
<div class="col-span-2 xl:col-span-1">
|
||||
<p
|
||||
v-if="title"
|
||||
class="text-base text-woot-500 dark:text-woot-500 mb-0 font-medium"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
<p
|
||||
class="text-sm mb-2 text-slate-700 dark:text-slate-300 leading-5 tracking-normal mt-2"
|
||||
>
|
||||
<slot v-if="subTitle" name="subTitle">
|
||||
{{ subTitle }}
|
||||
</slot>
|
||||
</p>
|
||||
<p v-if="note">
|
||||
<span class="font-semibold">{{ $t('INBOX_MGMT.NOTE') }}</span>
|
||||
{{ note }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-span-4 lg:col-span-4 2xl:col-span-3">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -23,17 +23,16 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-slate-75 dark:border-slate-600"
|
||||
class="flex flex-col min-w-[15rem] max-h-[21.25rem] max-w-[23.75rem] rounded-md border border-solid border-n-strong"
|
||||
:class="{
|
||||
'bg-woot-25 dark:bg-slate-700 border border-solid border-woot-300 dark:border-woot-400':
|
||||
'bg-woot-25 dark:bg-n-solid-2 border border-solid border-n-blue-border':
|
||||
active,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="flex justify-between items-center px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 rounded-t-[5px] border-b border-solid border-slate-50 dark:border-slate-600"
|
||||
class="flex justify-between items-center rounded-t-md px-2 w-full h-10 bg-slate-50 dark:bg-slate-900 border-b border-solid border-n-strong"
|
||||
:class="{
|
||||
'bg-woot-50 border-b border-solid border-woot-75 dark:border-woot-700':
|
||||
active,
|
||||
'bg-woot-50 border-b border-solid border-n-blue-border': active,
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center p-1 text-sm font-medium">{{ heading }}</div>
|
||||
|
||||
@@ -38,20 +38,20 @@ export default {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
height: 19px;
|
||||
height: 1.188rem;
|
||||
position: relative;
|
||||
transition-duration: 200ms;
|
||||
transition-property: background-color;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
width: 34px;
|
||||
width: 2.125rem;
|
||||
|
||||
&.active {
|
||||
background-color: var(--w-500);
|
||||
}
|
||||
|
||||
&.small {
|
||||
width: 22px;
|
||||
height: 14px;
|
||||
width: 1.375rem;
|
||||
height: 0.875rem;
|
||||
|
||||
span {
|
||||
height: var(--space-one);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, provide, onMounted, computed } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ref, useTemplateRef, provide, computed, watch } from 'vue';
|
||||
import { useElementSize } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
index: {
|
||||
@@ -19,6 +19,12 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const tabsContainer = useTemplateRef('tabsContainer');
|
||||
const tabsList = useTemplateRef('tabsList');
|
||||
|
||||
const { width: containerWidth } = useElementSize(tabsContainer);
|
||||
const { width: listWidth } = useElementSize(tabsList);
|
||||
|
||||
const hasScroll = ref(false);
|
||||
|
||||
const activeIndex = computed({
|
||||
@@ -34,20 +40,16 @@ provide('updateActiveIndex', index => {
|
||||
});
|
||||
|
||||
const computeScrollWidth = () => {
|
||||
// TODO: use useElementSize from vueuse
|
||||
const tabElement = document.querySelector('.tabs');
|
||||
if (tabElement) {
|
||||
hasScroll.value = tabElement.scrollWidth > tabElement.clientWidth;
|
||||
if (tabsContainer.value && tabsList.value) {
|
||||
hasScroll.value = tabsList.value.scrollWidth > tabsList.value.clientWidth;
|
||||
}
|
||||
};
|
||||
|
||||
const onScrollClick = direction => {
|
||||
// TODO: use useElementSize from vueuse
|
||||
const tabElement = document.querySelector('.tabs');
|
||||
if (tabElement) {
|
||||
let scrollPosition = tabElement.scrollLeft;
|
||||
if (tabsContainer.value && tabsList.value) {
|
||||
let scrollPosition = tabsList.value.scrollLeft;
|
||||
scrollPosition += direction === 'left' ? -100 : 100;
|
||||
tabElement.scrollTo({
|
||||
tabsList.value.scrollTo({
|
||||
top: 0,
|
||||
left: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
@@ -55,14 +57,19 @@ const onScrollClick = direction => {
|
||||
}
|
||||
};
|
||||
|
||||
useEventListener(window, 'resize', computeScrollWidth);
|
||||
onMounted(() => {
|
||||
computeScrollWidth();
|
||||
});
|
||||
// Watch for changes in element sizes with immediate execution
|
||||
watch(
|
||||
[containerWidth, listWidth],
|
||||
() => {
|
||||
computeScrollWidth();
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="tabsContainer"
|
||||
:class="{
|
||||
'tabs--container--with-border': border,
|
||||
'tabs--container--compact': isCompact,
|
||||
@@ -76,7 +83,7 @@ onMounted(() => {
|
||||
>
|
||||
<fluent-icon icon="chevron-left" :size="16" />
|
||||
</button>
|
||||
<ul :class="{ 'tabs--with-scroll': hasScroll }" class="tabs">
|
||||
<ul ref="tabsList" :class="{ 'tabs--with-scroll': hasScroll }" class="tabs">
|
||||
<slot />
|
||||
</ul>
|
||||
<button
|
||||
|
||||
@@ -72,26 +72,26 @@ export default {
|
||||
|
||||
&.active {
|
||||
h3 {
|
||||
@apply text-woot-500 dark:text-woot-500;
|
||||
@apply text-n-blue-text dark:text-n-blue-text;
|
||||
}
|
||||
|
||||
.step {
|
||||
@apply bg-woot-500 dark:bg-woot-500;
|
||||
@apply bg-n-brand dark:bg-n-brand;
|
||||
}
|
||||
}
|
||||
|
||||
&.over {
|
||||
&::after {
|
||||
@apply bg-woot-500 dark:bg-woot-500;
|
||||
@apply bg-n-brand dark:bg-n-brand;
|
||||
}
|
||||
|
||||
.step {
|
||||
@apply bg-woot-500 dark:bg-woot-500;
|
||||
@apply bg-n-brand dark:bg-n-brand;
|
||||
}
|
||||
|
||||
& + .item {
|
||||
&::before {
|
||||
@apply bg-woot-500 dark:bg-woot-500;
|
||||
@apply bg-n-brand dark:bg-n-brand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,10 +201,10 @@ export default {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.filter {
|
||||
@apply bg-slate-50 dark:bg-slate-800 p-2 border border-solid border-slate-75 dark:border-slate-600 rounded-md mb-2;
|
||||
@apply bg-n-slate-3 dark:bg-n-solid-3 p-2 border border-solid border-n-strong dark:border-n-strong rounded-md mb-2;
|
||||
|
||||
&.is-a-macro {
|
||||
@apply mb-0 bg-white dark:bg-slate-700 p-0 border-0 rounded-none;
|
||||
@apply mb-0 bg-n-slate-2 dark:bg-n-solid-3 p-0 border-0 rounded-none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ export default {
|
||||
getInputErrorClass(errorMessage) {
|
||||
return errorMessage
|
||||
? 'bg-red-50 dark:bg-red-800/50 border-red-100 dark:border-red-700/50'
|
||||
: 'bg-slate-50 dark:bg-slate-800 border-slate-75 dark:border-slate-700/50';
|
||||
: 'bg-n-slate-3 dark:bg-n-solid-3 border-n-strong dark:border-n-strong';
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pt-4 pb-0 px-8 border-b border-solid border-n-weak">
|
||||
<div class="pt-4 pb-0 px-8 border-b border-solid border-n-weak/60">
|
||||
<h2 class="text-2xl text-slate-800 dark:text-slate-100 mb-1 font-medium">
|
||||
{{ headerTitle }}
|
||||
</h2>
|
||||
|
||||
@@ -85,7 +85,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-between h-[52px] gap-2 ltr:pl-3 rtl:pr-3">
|
||||
<div class="flex justify-between h-[3.25rem] gap-2 ltr:pl-3 rtl:pr-3">
|
||||
<EditorModeToggle
|
||||
:mode="mode"
|
||||
class="mt-3"
|
||||
|
||||
@@ -411,7 +411,7 @@ export default {
|
||||
}
|
||||
|
||||
.checkbox-wrapper {
|
||||
@apply h-10 w-10 flex items-center justify-center rounded-full cursor-pointer mt-4;
|
||||
@apply flex items-center justify-center rounded-full cursor-pointer mt-4;
|
||||
|
||||
input[type='checkbox'] {
|
||||
@apply m-0 cursor-pointer;
|
||||
|
||||
@@ -53,7 +53,7 @@ const showCopilotTab = computed(() =>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="ltr:border-l rtl:border-r border-n-weak h-full overflow-hidden z-10 min-w-[320px] w-[320px] 2xl:min-w-96 2xl:w-96 flex flex-col bg-n-background"
|
||||
class="ltr:border-l rtl:border-r border-n-weak h-full overflow-hidden z-10 w-80 min-w-80 2xl:min-w-96 2xl:w-96 flex flex-col bg-n-background"
|
||||
>
|
||||
<div v-if="showCopilotTab" class="p-2">
|
||||
<TabBar
|
||||
|
||||
@@ -45,7 +45,7 @@ const statusDesiredOrder = [
|
||||
];
|
||||
|
||||
const isCreating = ref(false);
|
||||
const inputStyles = { borderRadius: '12px', fontSize: '14px' };
|
||||
const inputStyles = { borderRadius: '0.75rem', fontSize: '0.875rem' };
|
||||
|
||||
const formState = reactive({
|
||||
title: '',
|
||||
@@ -209,7 +209,7 @@ onMounted(getTeams);
|
||||
v-model="formState.title"
|
||||
:class="{ error: v$.title.$error }"
|
||||
class="w-full"
|
||||
:styles="{ ...inputStyles, padding: '6px 12px' }"
|
||||
:styles="{ ...inputStyles, padding: '0.375rem 0.75rem' }"
|
||||
:label="$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TITLE.LABEL')"
|
||||
:placeholder="
|
||||
$t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.TITLE.PLACEHOLDER')
|
||||
@@ -221,7 +221,7 @@ onMounted(getTeams);
|
||||
{{ $t('INTEGRATION_SETTINGS.LINEAR.ADD_OR_LINK.FORM.DESCRIPTION.LABEL') }}
|
||||
<textarea
|
||||
v-model="formState.description"
|
||||
:style="{ ...inputStyles, padding: '8px 12px' }"
|
||||
:style="{ ...inputStyles, padding: '0.5rem 0.75rem' }"
|
||||
rows="3"
|
||||
class="text-sm"
|
||||
:placeholder="
|
||||
|
||||
175
app/javascript/dashboard/composables/spec/useFontSize.spec.js
Normal file
175
app/javascript/dashboard/composables/spec/useFontSize.spec.js
Normal file
@@ -0,0 +1,175 @@
|
||||
import { ref } from 'vue';
|
||||
import { useFontSize } from '../useFontSize';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('dashboard/composables/useUISettings');
|
||||
vi.mock('dashboard/composables', () => ({
|
||||
useAlert: vi.fn(message => message),
|
||||
}));
|
||||
vi.mock('vue-i18n');
|
||||
|
||||
// Mock requestAnimationFrame
|
||||
global.requestAnimationFrame = vi.fn(cb => cb());
|
||||
|
||||
describe('useFontSize', () => {
|
||||
const mockUISettings = ref({
|
||||
font_size: '16px',
|
||||
});
|
||||
const mockUpdateUISettings = vi.fn().mockResolvedValue(undefined);
|
||||
const mockTranslate = vi.fn(key => key);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
// Setup mocks
|
||||
useUISettings.mockReturnValue({
|
||||
uiSettings: mockUISettings,
|
||||
updateUISettings: mockUpdateUISettings,
|
||||
});
|
||||
|
||||
useI18n.mockReturnValue({
|
||||
t: mockTranslate,
|
||||
});
|
||||
|
||||
// Reset DOM state
|
||||
document.documentElement.style.removeProperty('font-size');
|
||||
|
||||
// Reset mockUISettings to default
|
||||
mockUISettings.value = { font_size: '16px' };
|
||||
});
|
||||
|
||||
it('returns fontSizeOptions with correct structure', () => {
|
||||
const { fontSizeOptions } = useFontSize();
|
||||
expect(fontSizeOptions).toHaveLength(6);
|
||||
expect(fontSizeOptions[0]).toHaveProperty('value');
|
||||
expect(fontSizeOptions[0]).toHaveProperty('label');
|
||||
|
||||
// Check specific options
|
||||
expect(fontSizeOptions.find(option => option.value === '16px')).toEqual({
|
||||
value: '16px',
|
||||
label:
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.DEFAULT',
|
||||
});
|
||||
|
||||
expect(fontSizeOptions.find(option => option.value === '14px')).toEqual({
|
||||
value: '14px',
|
||||
label:
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.SMALLER',
|
||||
});
|
||||
|
||||
expect(fontSizeOptions.find(option => option.value === '22px')).toEqual({
|
||||
value: '22px',
|
||||
label:
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.EXTRA_LARGE',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns currentFontSize from UI settings', () => {
|
||||
const { currentFontSize } = useFontSize();
|
||||
expect(currentFontSize.value).toBe('16px');
|
||||
|
||||
mockUISettings.value.font_size = '18px';
|
||||
expect(currentFontSize.value).toBe('18px');
|
||||
});
|
||||
|
||||
it('applies font size to document root correctly based on pixel values', () => {
|
||||
const { applyFontSize } = useFontSize();
|
||||
|
||||
applyFontSize('18px');
|
||||
expect(document.documentElement.style.fontSize).toBe('18px');
|
||||
|
||||
applyFontSize('14px');
|
||||
expect(document.documentElement.style.fontSize).toBe('14px');
|
||||
|
||||
applyFontSize('22px');
|
||||
expect(document.documentElement.style.fontSize).toBe('22px');
|
||||
|
||||
applyFontSize('16px');
|
||||
expect(document.documentElement.style.fontSize).toBe('16px');
|
||||
});
|
||||
|
||||
it('updates UI settings and applies font size', async () => {
|
||||
const { updateFontSize } = useFontSize();
|
||||
|
||||
await updateFontSize('20px');
|
||||
|
||||
expect(mockUpdateUISettings).toHaveBeenCalledWith({ font_size: '20px' });
|
||||
expect(document.documentElement.style.fontSize).toBe('20px');
|
||||
expect(useAlert).toHaveBeenCalledWith(
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.UPDATE_SUCCESS'
|
||||
);
|
||||
});
|
||||
|
||||
it('shows error alert when update fails', async () => {
|
||||
mockUpdateUISettings.mockRejectedValueOnce(new Error('Update failed'));
|
||||
|
||||
const { updateFontSize } = useFontSize();
|
||||
await updateFontSize('20px');
|
||||
|
||||
expect(useAlert).toHaveBeenCalledWith(
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.UPDATE_ERROR'
|
||||
);
|
||||
});
|
||||
|
||||
it('handles unknown font size values gracefully', () => {
|
||||
const { applyFontSize } = useFontSize();
|
||||
|
||||
// Should not throw an error and should apply the default font size
|
||||
applyFontSize('unknown-size');
|
||||
expect(document.documentElement.style.fontSize).toBe('16px');
|
||||
});
|
||||
|
||||
it('watches for UI settings changes and applies font size', async () => {
|
||||
useFontSize();
|
||||
|
||||
// Initial font size should now be 16px instead of empty
|
||||
expect(document.documentElement.style.fontSize).toBe('16px');
|
||||
|
||||
// Update UI settings
|
||||
mockUISettings.value = { font_size: '18px' };
|
||||
|
||||
// Wait for next tick to let watchers fire
|
||||
await Promise.resolve();
|
||||
|
||||
expect(document.documentElement.style.fontSize).toBe('18px');
|
||||
});
|
||||
|
||||
it('translates font size option labels correctly', () => {
|
||||
// Set up specific translation mapping
|
||||
mockTranslate.mockImplementation(key => {
|
||||
const translations = {
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.SMALLER':
|
||||
'Smaller',
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.DEFAULT':
|
||||
'Default',
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.EXTRA_LARGE':
|
||||
'Extra Large',
|
||||
};
|
||||
return translations[key] || key;
|
||||
});
|
||||
|
||||
const { fontSizeOptions } = useFontSize();
|
||||
|
||||
// Check that translation is applied
|
||||
expect(fontSizeOptions.find(option => option.value === '14px').label).toBe(
|
||||
'Smaller'
|
||||
);
|
||||
expect(fontSizeOptions.find(option => option.value === '16px').label).toBe(
|
||||
'Default'
|
||||
);
|
||||
expect(fontSizeOptions.find(option => option.value === '22px').label).toBe(
|
||||
'Extra Large'
|
||||
);
|
||||
|
||||
// Verify translation function was called with correct keys
|
||||
expect(mockTranslate).toHaveBeenCalledWith(
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.SMALLER'
|
||||
);
|
||||
expect(mockTranslate).toHaveBeenCalledWith(
|
||||
'PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.DEFAULT'
|
||||
);
|
||||
});
|
||||
});
|
||||
140
app/javascript/dashboard/composables/useFontSize.js
Normal file
140
app/javascript/dashboard/composables/useFontSize.js
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file useFontSize.js
|
||||
* @description A composable for managing font size settings throughout the application.
|
||||
* This handles font size selection, application to the DOM, and persistence in user settings.
|
||||
*/
|
||||
|
||||
import { computed, watch } from 'vue';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
/**
|
||||
* Font size options with their pixel values
|
||||
* @type {Object}
|
||||
*/
|
||||
const FONT_SIZE_OPTIONS = {
|
||||
SMALLER: '14px',
|
||||
SMALL: '15px',
|
||||
DEFAULT: '16px',
|
||||
LARGE: '18px',
|
||||
LARGER: '20px',
|
||||
EXTRA_LARGE: '22px',
|
||||
};
|
||||
|
||||
/**
|
||||
* Array of font size option keys
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
const FONT_SIZE_NAMES = Object.keys(FONT_SIZE_OPTIONS);
|
||||
|
||||
/**
|
||||
* Get font size label translation key
|
||||
*
|
||||
* @param {string} name - Font size name
|
||||
* @returns {string} Translation key
|
||||
*/
|
||||
const getFontSizeLabelKey = name =>
|
||||
`PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.OPTIONS.${name}`;
|
||||
|
||||
/**
|
||||
* Create font size option object
|
||||
*
|
||||
* @param {Function} t - Translation function
|
||||
* @param {string} name - Font size name
|
||||
* @returns {Object} Font size option with value and label
|
||||
*/
|
||||
const createFontSizeOption = (t, name) => ({
|
||||
value: FONT_SIZE_OPTIONS[name],
|
||||
label: t(getFontSizeLabelKey(name)),
|
||||
});
|
||||
|
||||
/**
|
||||
* Apply font size value to document root
|
||||
*
|
||||
* @param {string} pixelValue - Font size value in pixels
|
||||
*/
|
||||
const applyFontSizeToDOM = pixelValue => {
|
||||
document.documentElement.style.setProperty(
|
||||
'font-size',
|
||||
pixelValue ?? FONT_SIZE_OPTIONS.DEFAULT
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Font size management composable
|
||||
*
|
||||
* @returns {Object} Font size utilities and state
|
||||
* @property {Array} fontSizeOptions - Array of font size options for select components
|
||||
* @property {import('vue').ComputedRef<string>} currentFontSize - Current font size from UI settings
|
||||
* @property {Function} applyFontSize - Function to apply font size to document
|
||||
* @property {Function} updateFontSize - Function to update font size in settings with alert feedback
|
||||
*/
|
||||
export const useFontSize = () => {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
const { t } = useI18n();
|
||||
|
||||
/**
|
||||
* Font size options for select dropdown
|
||||
* @type {Array<{value: string, label: string}>}
|
||||
*/
|
||||
const fontSizeOptions = FONT_SIZE_NAMES.map(name =>
|
||||
createFontSizeOption(t, name)
|
||||
);
|
||||
|
||||
/**
|
||||
* Current font size from UI settings
|
||||
* @type {import('vue').ComputedRef<string>}
|
||||
*/
|
||||
const currentFontSize = computed(
|
||||
() => uiSettings.value.font_size || FONT_SIZE_OPTIONS.DEFAULT
|
||||
);
|
||||
|
||||
/**
|
||||
* Apply font size to document root
|
||||
* @param {string} pixelValue - Font size in pixels (e.g., '16px')
|
||||
* @returns {void}
|
||||
*/
|
||||
const applyFontSize = pixelValue => {
|
||||
// Use requestAnimationFrame for better performance
|
||||
requestAnimationFrame(() => applyFontSizeToDOM(pixelValue));
|
||||
};
|
||||
|
||||
/**
|
||||
* Update font size in settings and apply to document
|
||||
* Shows success/error alerts
|
||||
* @param {string} pixelValue - Font size in pixels (e.g., '16px')
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const updateFontSize = async pixelValue => {
|
||||
try {
|
||||
await updateUISettings({ font_size: pixelValue });
|
||||
applyFontSize(pixelValue);
|
||||
useAlert(
|
||||
t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.UPDATE_SUCCESS')
|
||||
);
|
||||
} catch (error) {
|
||||
useAlert(
|
||||
t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.UPDATE_ERROR')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Watch for changes to the font size in UI settings
|
||||
watch(
|
||||
() => uiSettings.value.font_size,
|
||||
newSize => {
|
||||
applyFontSize(newSize);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
fontSizeOptions,
|
||||
currentFontSize,
|
||||
applyFontSize,
|
||||
updateFontSize,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFontSize;
|
||||
@@ -9,9 +9,9 @@ export const setColorTheme = isOSOnDarkMode => {
|
||||
selectedColorScheme === 'dark'
|
||||
) {
|
||||
document.body.classList.add('dark');
|
||||
document.documentElement.setAttribute('style', 'color-scheme: dark;');
|
||||
document.documentElement.style.setProperty('color-scheme', 'dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
document.documentElement.setAttribute('style', 'color-scheme: light;');
|
||||
document.documentElement.style.setProperty('color-scheme', 'light');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -35,6 +35,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"INTERFACE_SECTION": {
|
||||
"TITLE": "Interface",
|
||||
"NOTE": "Customize the look and feel of your Chatwoot dashboard.",
|
||||
"FONT_SIZE": {
|
||||
"TITLE": "Font size",
|
||||
"NOTE": "Adjust the text size across the dashboard based on your preference.",
|
||||
"UPDATE_SUCCESS": "Your font settings have been updated successfully",
|
||||
"UPDATE_ERROR": "There is an error while updating the font settings, please try again",
|
||||
"OPTIONS": {
|
||||
"SMALLER": "Smaller",
|
||||
"SMALL": "Small",
|
||||
"DEFAULT": "Default",
|
||||
"LARGE": "Large",
|
||||
"LARGER": "Larger",
|
||||
"EXTRA_LARGE": "Extra Large"
|
||||
}
|
||||
}
|
||||
},
|
||||
"MESSAGE_SIGNATURE_SECTION": {
|
||||
"TITLE": "Personal message signature",
|
||||
"NOTE": "Create a unique message signature to appear at the end of every message you send from any inbox. You can also include an inline image, which is supported in live-chat, email, and API inboxes.",
|
||||
|
||||
@@ -140,7 +140,7 @@ export default {
|
||||
</div>
|
||||
<div
|
||||
v-if="isWidgetVisible"
|
||||
class="widget-wrapper flex flex-col justify-between rounded-lg shadow-md bg-slate-25 dark:bg-slate-800 h-[500px] w-[320px]"
|
||||
class="widget-wrapper flex flex-col justify-between rounded-lg shadow-md bg-slate-25 dark:bg-slate-800 h-[31.25rem] w-80"
|
||||
>
|
||||
<WidgetHead :config="getWidgetConfig" />
|
||||
<div>
|
||||
|
||||
@@ -195,7 +195,7 @@ export default {
|
||||
{{ $t('AUTOMATION.ADD.FORM.CONDITIONS.LABEL') }}
|
||||
</label>
|
||||
<div
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-slate-25 dark:bg-slate-700 border-slate-50 dark:border-slate-700"
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-n-slate-2 dark:bg-n-solid-2 border-n-strong"
|
||||
>
|
||||
<FilterInputBox
|
||||
v-for="(condition, i) in automation.conditions"
|
||||
@@ -262,7 +262,7 @@ export default {
|
||||
{{ $t('AUTOMATION.ADD.FORM.ACTIONS.LABEL') }}
|
||||
</label>
|
||||
<div
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-slate-25 dark:bg-slate-700 border-slate-50 dark:border-slate-700"
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-n-slate-2 dark:bg-n-solid-2 border-n-strong"
|
||||
>
|
||||
<AutomationActionInput
|
||||
v-for="(action, i) in automation.actions"
|
||||
|
||||
@@ -178,7 +178,7 @@ export default {
|
||||
{{ $t('AUTOMATION.ADD.FORM.CONDITIONS.LABEL') }}
|
||||
</label>
|
||||
<div
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-slate-25 dark:bg-slate-700 border-slate-50 dark:border-slate-700"
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-n-slate-2 dark:bg-n-solid-2 border-n-strong"
|
||||
>
|
||||
<FilterInputBox
|
||||
v-for="(condition, i) in automation.conditions"
|
||||
@@ -245,7 +245,7 @@ export default {
|
||||
{{ $t('AUTOMATION.ADD.FORM.ACTIONS.LABEL') }}
|
||||
</label>
|
||||
<div
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-slate-25 dark:bg-slate-700 border-slate-50 dark:border-slate-700"
|
||||
class="w-full p-4 mb-4 border border-solid rounded-lg bg-n-slate-2 dark:bg-n-solid-2 border-n-strong"
|
||||
>
|
||||
<AutomationActionInput
|
||||
v-for="(action, i) in automation.actions"
|
||||
|
||||
@@ -12,7 +12,7 @@ defineProps({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid grid-cols-[1fr_200px] gap-5">
|
||||
<div class="grid grid-cols-[1fr_auto] gap-5">
|
||||
<div>
|
||||
<span class="text-base font-medium text-n-slate-12">
|
||||
{{ title }}
|
||||
|
||||
@@ -30,7 +30,7 @@ defineProps({
|
||||
<slot name="label" />
|
||||
</div>
|
||||
<p
|
||||
class="text-base text-slate-600 dark:text-slate-300 max-w-[400px] w-full line-clamp-2"
|
||||
class="text-base text-slate-600 dark:text-slate-300 max-w-[25rem] w-full line-clamp-2"
|
||||
>
|
||||
<slot name="description">
|
||||
{{ description }}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-row overflow-auto p-4 h-full bg-slate-25 dark:bg-slate-800"
|
||||
class="flex flex-row overflow-auto p-4 h-full bg-n-alpha-2 dark:bg-n-solid-1"
|
||||
>
|
||||
<woot-wizard
|
||||
class="hidden md:block w-1/4"
|
||||
|
||||
@@ -105,14 +105,14 @@ const openDelete = inbox => {
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="w-12 h-12 bg-black-50 dark:bg-black-800 rounded-full p-2 ring ring-opacity-20 dark:ring-opacity-80 ring-black-100 dark:ring-black-900 border border-slate-100 dark:border-slate-700/50 shadow-sm block"
|
||||
class="w-[48px] h-[48px] flex justify-center items-center bg-black-50 dark:bg-black-800 rounded-full p-2 ring ring-opacity-20 dark:ring-opacity-80 ring-black-100 dark:ring-black-900 border border-slate-100 dark:border-slate-700/50 shadow-sm"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 24 24"
|
||||
class="opacity-80 p-1"
|
||||
class="opacity-80 p-1 flex-shrink-0"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
|
||||
@@ -362,7 +362,7 @@ export default {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex-grow flex-shrink w-full min-w-0 pl-0 pr-0 overflow-auto bg-white settings dark:bg-slate-800"
|
||||
class="flex-grow flex-shrink w-full min-w-0 pl-0 pr-0 overflow-auto settings bg-n-solid-1"
|
||||
>
|
||||
<SettingIntroBanner
|
||||
:header-image="inbox.avatarUrl"
|
||||
|
||||
@@ -413,7 +413,10 @@ export default {
|
||||
:widget-bubble-type="widgetBubbleType"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="mx-5 p-2.5 bg-slate-50 rounded-lg dark:bg-slate-700">
|
||||
<div
|
||||
v-else
|
||||
class="mx-5 p-2.5 bg-n-slate-3 rounded-lg dark:bg-n-solid-3"
|
||||
>
|
||||
<woot-code :script="widgetScript" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-row items-center gap-4">
|
||||
<div class="flex flex-col lg:flex-row items-start lg:items-center gap-4">
|
||||
<button
|
||||
v-for="keyOption in senderNameKeyOptions"
|
||||
:key="keyOption.key"
|
||||
|
||||
@@ -67,7 +67,7 @@ const dropdownValues = () => {
|
||||
:class="
|
||||
errorKey
|
||||
? 'bg-red-50 animate-shake dark:bg-red-800'
|
||||
: 'bg-white dark:bg-slate-700'
|
||||
: 'bg-n-slate-2 dark:bg-n-solid-3'
|
||||
"
|
||||
>
|
||||
<ActionInput
|
||||
@@ -89,6 +89,7 @@ const dropdownValues = () => {
|
||||
size="small"
|
||||
variant="smooth"
|
||||
color-scheme="alert"
|
||||
class="flex-shrink-0"
|
||||
@click="$emit('deleteNode')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -91,9 +91,9 @@ export default {
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mt-2 flex items-start p-2 bg-slate-50 dark:bg-slate-700 rounded-md"
|
||||
class="mt-2 flex items-start p-2 bg-n-slate-3 dark:bg-n-solid-3 rounded-md"
|
||||
>
|
||||
<fluent-icon icon="info" size="20" class="flex-shrink" />
|
||||
<fluent-icon icon="info" size="16" class="flex-shrink-0 mt-0.5" />
|
||||
<p
|
||||
class="ml-2 rtl:ml-0 rtl:mr-2 mb-0 text-slate-600 dark:text-slate-200"
|
||||
>
|
||||
|
||||
@@ -86,7 +86,7 @@ const playAudio = async () => {
|
||||
v-tooltip.top="
|
||||
$t('PROFILE_SETTINGS.FORM.AUDIO_NOTIFICATIONS_SECTION.PLAY')
|
||||
"
|
||||
class="border-0 shadow-sm outline-none flex justify-center items-center size-10 appearance-none rounded-xl ring-ash-200 ring-1 ring-inset focus:ring-2 focus:ring-inset focus:ring-primary-500 flex-shrink-0 mt-[28px]"
|
||||
class="border-0 shadow-sm outline-none flex justify-center items-center size-10 appearance-none rounded-xl ring-ash-200 ring-1 ring-inset focus:ring-2 focus:ring-inset focus:ring-primary-500 flex-shrink-0 mt-[1.75rem]"
|
||||
@click="playAudio"
|
||||
>
|
||||
<Icon icon="i-lucide-volume-2" />
|
||||
|
||||
@@ -20,10 +20,10 @@ export default {
|
||||
isPasswordChanging: false,
|
||||
errorMessage: '',
|
||||
inputStyles: {
|
||||
borderRadius: '12px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '14px',
|
||||
marginBottom: '2px',
|
||||
borderRadius: '0.75rem',
|
||||
padding: '0.375rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
marginBottom: '0.125rem',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import FormSelect from 'v3/components/Form/Select.vue';
|
||||
import { useFontSize } from 'dashboard/composables/useFontSize';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const { fontSizeOptions } = useFontSize();
|
||||
|
||||
const selectedValue = computed({
|
||||
get: () => props.value,
|
||||
set: value => {
|
||||
emit('change', value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex gap-2 justify-between w-full items-start">
|
||||
<div>
|
||||
<label class="text-n-gray-12 font-medium leading-6 text-sm">
|
||||
{{ label }}
|
||||
</label>
|
||||
<p class="text-n-gray-11">
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
<FormSelect
|
||||
v-model="selectedValue"
|
||||
name="fontSize"
|
||||
spacing="compact"
|
||||
class="min-w-28 mt-px"
|
||||
:value="selectedValue"
|
||||
:options="fontSizeOptions"
|
||||
label=""
|
||||
>
|
||||
<option
|
||||
v-for="option in fontSizeOptions"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:selected="option.value === selectedValue"
|
||||
>
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</FormSelect>
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,15 +25,17 @@ defineProps({
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="flex flex-col gap-4 w-full h-fit sm:max-h-[220px] p-4 sm:max-w-[350px] rounded-md border border-solid border-ash-200"
|
||||
class="flex flex-col gap-4 w-full h-fit p-4 rounded-md border border-n-weak dark:border-n-weak"
|
||||
:class="{
|
||||
'border-primary-300 ': active,
|
||||
}"
|
||||
>
|
||||
<div class="flex flex-col gap-2 items-center w-full rounded-t-[5px]">
|
||||
<div class="flex items-center justify-between w-full gap-1">
|
||||
<div class="flex items-center text-base font-medium text-ash-900">
|
||||
{{ title }}
|
||||
<div class="grid grid-cols-[1fr_auto] items-center w-full gap-1">
|
||||
<div
|
||||
class="overflow-hidden text-base font-medium text-ash-900 text-left"
|
||||
>
|
||||
<span class="block truncate">{{ title }}</span>
|
||||
</div>
|
||||
<input
|
||||
:checked="active"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useFontSize } from 'dashboard/composables/useFontSize';
|
||||
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
|
||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||
import { parseAPIErrorResponse } from 'dashboard/store/utils/api';
|
||||
@@ -9,6 +10,7 @@ import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import UserProfilePicture from './UserProfilePicture.vue';
|
||||
import UserBasicDetails from './UserBasicDetails.vue';
|
||||
import MessageSignature from './MessageSignature.vue';
|
||||
import FontSize from './FontSize.vue';
|
||||
import HotKeyCard from './HotKeyCard.vue';
|
||||
import ChangePassword from './ChangePassword.vue';
|
||||
import NotificationPreferences from './NotificationPreferences.vue';
|
||||
@@ -25,6 +27,7 @@ export default {
|
||||
components: {
|
||||
MessageSignature,
|
||||
FormSection,
|
||||
FontSize,
|
||||
UserProfilePicture,
|
||||
Policy,
|
||||
UserBasicDetails,
|
||||
@@ -36,12 +39,12 @@ export default {
|
||||
},
|
||||
mixins: [globalConfigMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings, isEditorHotKeyEnabled } =
|
||||
useUISettings();
|
||||
const { isEditorHotKeyEnabled } = useUISettings();
|
||||
const { currentFontSize, updateFontSize } = useFontSize();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
currentFontSize,
|
||||
updateFontSize,
|
||||
isEditorHotKeyEnabled,
|
||||
};
|
||||
},
|
||||
@@ -182,7 +185,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid py-16 px-5 font-inter mx-auto gap-16 sm:max-w-[720px]">
|
||||
<div class="grid py-16 px-5 font-inter mx-auto gap-16 sm:max-w-screen-md">
|
||||
<div class="flex flex-col gap-6">
|
||||
<h2 class="text-2xl font-medium text-ash-900">
|
||||
{{ $t('PROFILE_SETTINGS.TITLE') }}
|
||||
@@ -201,7 +204,19 @@ export default {
|
||||
@update-user="updateProfile"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormSection
|
||||
:title="$t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.TITLE')"
|
||||
:description="$t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.NOTE')"
|
||||
>
|
||||
<FontSize
|
||||
:value="currentFontSize"
|
||||
:label="$t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.TITLE')"
|
||||
:description="
|
||||
$t('PROFILE_SETTINGS.FORM.INTERFACE_SECTION.FONT_SIZE.NOTE')
|
||||
"
|
||||
@change="updateFontSize"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
:title="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.TITLE')"
|
||||
:description="$t('PROFILE_SETTINGS.FORM.MESSAGE_SIGNATURE_SECTION.NOTE')"
|
||||
@@ -221,7 +236,7 @@ export default {
|
||||
<button
|
||||
v-for="hotKey in hotKeys"
|
||||
:key="hotKey.key"
|
||||
class="px-0 reset-base"
|
||||
class="px-0 reset-base w-full sm:flex-1"
|
||||
>
|
||||
<HotKeyCard
|
||||
:key="hotKey.title"
|
||||
|
||||
@@ -35,10 +35,10 @@ export default {
|
||||
userDisplayName: this.displayName,
|
||||
userEmail: this.email,
|
||||
inputStyles: {
|
||||
borderRadius: '12px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '14px',
|
||||
marginBottom: '2px',
|
||||
borderRadius: '0.75rem',
|
||||
padding: '0.375rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
marginBottom: '0.125rem',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="reports--wrapper overflow-auto bg-n-background w-full px-8 xl:px-0"
|
||||
>
|
||||
<div class="max-w-[960px] mx-auto pb-12">
|
||||
<div class="max-w-[60rem] mx-auto pb-12">
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -161,9 +161,9 @@ export default {
|
||||
:class="{ error: v$.name.$error }"
|
||||
class="w-full"
|
||||
:styles="{
|
||||
borderRadius: '12px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '14px',
|
||||
borderRadius: '0.75rem',
|
||||
padding: '0.375rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
}"
|
||||
:label="$t('SLA.FORM.NAME.LABEL')"
|
||||
:placeholder="$t('SLA.FORM.NAME.PLACEHOLDER')"
|
||||
@@ -175,9 +175,9 @@ export default {
|
||||
v-model="description"
|
||||
class="w-full"
|
||||
:styles="{
|
||||
borderRadius: '12px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '14px',
|
||||
borderRadius: '0.75rem',
|
||||
padding: '0.375rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
}"
|
||||
:label="$t('SLA.FORM.DESCRIPTION.LABEL')"
|
||||
:placeholder="$t('SLA.FORM.DESCRIPTION.PLACEHOLDER')"
|
||||
|
||||
@@ -91,9 +91,9 @@ export default {
|
||||
:class="{ error: v$.thresholdTime.$error }"
|
||||
class="flex-grow"
|
||||
:styles="{
|
||||
borderRadius: '12px',
|
||||
padding: '6px 12px',
|
||||
fontSize: '14px',
|
||||
borderRadius: '0.75rem',
|
||||
padding: '0.375rem 0.75rem',
|
||||
fontSize: '0.875rem',
|
||||
}"
|
||||
:label="label"
|
||||
:placeholder="placeholder"
|
||||
|
||||
@@ -87,7 +87,7 @@ export default {
|
||||
<template>
|
||||
<div
|
||||
role="dialog"
|
||||
class="emoji-dialog bg-white shadow-lg dark:bg-slate-900 rounded-md border border-solid border-slate-75 dark:border-slate-800/50 box-content h-[300px] absolute right-0 -top-[95px] w-80 z-20"
|
||||
class="emoji-dialog bg-white shadow-lg dark:bg-slate-900 rounded-md border border-solid border-slate-75 dark:border-slate-800/50 box-content h-[18.75rem] absolute right-0 -top-[95px] w-80 z-20"
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2 emoji-search--wrap">
|
||||
@@ -230,7 +230,7 @@ export default {
|
||||
@apply box-border p-1;
|
||||
|
||||
.emoji--item {
|
||||
@apply h-[26px] w-[26px] leading-normal m-1;
|
||||
@apply h-[1.625rem] w-[1.625rem] leading-normal m-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ export default {
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
@apply items-center flex flex-col h-[212px] justify-center;
|
||||
@apply items-center flex flex-col h-[13.25rem] justify-center;
|
||||
|
||||
.emoji-icon {
|
||||
@apply text-slate-200 dark:text-slate-200 mb-2;
|
||||
@@ -255,7 +255,7 @@ export default {
|
||||
}
|
||||
|
||||
.emoji-item {
|
||||
@apply h-[212px] overflow-y-auto;
|
||||
@apply h-[13.25rem] overflow-y-auto;
|
||||
}
|
||||
|
||||
.emoji-category--title {
|
||||
@@ -263,7 +263,7 @@ export default {
|
||||
}
|
||||
|
||||
.emoji-dialog--footer {
|
||||
@apply relative w-[322px] -left-px rtl:left-[unset] rtl:-right-px bottom-0 py-0 rounded-b-md border-b border-solid border-slate-75 dark:border-slate-800/50 px-1 bg-slate-75 dark:bg-slate-800;
|
||||
@apply relative w-full py-0 rounded-b-[0.34rem] px-1 bg-slate-75 dark:bg-slate-800;
|
||||
|
||||
ul {
|
||||
@apply flex relative left-[2px] rtl:left-[unset] rtl:right-[2px] list-none m-0 overflow-auto py-1 px-0;
|
||||
|
||||
@@ -68,7 +68,7 @@ export default {
|
||||
'text-ash-900': modelValue,
|
||||
'pl-9': icon,
|
||||
}"
|
||||
class="block w-full px-3 py-2 pr-6 mb-0 border-0 shadow-sm outline-none appearance-none rounded-xl select-caret ring-ash-200 ring-1 ring-inset placeholder:text-ash-900 focus:ring-2 focus:ring-inset focus:ring-primary-500 sm:text-sm sm:leading-6"
|
||||
class="block w-full px-3 py-2 pr-6 mb-0 border-0 shadow-sm outline-none appearance-none rounded-xl select-caret ring-ash-200 ring-1 ring-inset placeholder:text-ash-900 focus:ring-2 focus:ring-inset focus:ring-primary-500 text-sm leading-6"
|
||||
@input="onInput"
|
||||
>
|
||||
<option value="" disabled selected class="hidden">
|
||||
|
||||
@@ -30,8 +30,8 @@ export default {
|
||||
class="rounded-full bg-white top-0.5 absolute dark:bg-white w-3 h-3 translate-y-0 duration-200 transition-transform ease-in-out"
|
||||
:class="
|
||||
modelValue
|
||||
? 'ltr:translate-x-0 rtl:translate-x-[12px]'
|
||||
: 'ltr:-translate-x-[12px] rtl:translate-x-0'
|
||||
? 'ltr:translate-x-0 rtl:translate-x-[0.75rem]'
|
||||
: 'ltr:-translate-x-[0.75rem] rtl:translate-x-0'
|
||||
"
|
||||
/>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user