diff --git a/CHANGELOG.md b/CHANGELOG.md index ca2064a..72ecc8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Theme presets: Tooltips can now be globally styled with the "default", "primevue", or "vuetify" presets. Switching themes at runtime is supported. + ## [1.2.2] - 2025-10-27 ### Fixed diff --git a/README.md b/README.md index f724920..c74a10c 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ const app = createApp(App) // Configure global defaults for all tooltips app.use(VueCustomTooltip, { + theme: 'default', // or 'vuetify' or 'primevue' globalConfig: { position: 'top', // Default position for all tooltips trigger: 'hover', // Default trigger behavior @@ -381,6 +382,59 @@ The `v-tooltip` directive is also fully typed when you install the plugin. TypeS ``` +## Theme Presets + +Vue Custom Tooltip supports built-in theme presets for easy integration with popular UI frameworks, as well as a default theme: + +- **default**: Uses the component's original built-in styles (no extra CSS loaded) +- **primevue**: Styles inspired by PrimeVue's design system +- **vuetify**: Styles inspired by Vuetify's Material Design implementation + +You can select a theme globally when registering the plugin: + +```typescript +import { VueCustomTooltip } from '@borstihd/vue-custom-tooltip' +import { createApp } from 'vue' +import App from './App.vue' +import '@borstihd/vue-custom-tooltip/dist/style.css' + +const app = createApp(App) + +// Use a theme preset +app.use(VueCustomTooltip, { + theme: 'primevue' // or 'vuetify' or 'default' +}) + +// The default theme is used if you omit the theme option: +app.use(VueCustomTooltip) // same as theme: 'default' + +app.mount('#app') +``` + +You can also switch themes at runtime: + +```typescript +import { setTooltipGlobalTheme } from '@borstihd/vue-custom-tooltip' + +setTooltipGlobalTheme('vuetify') // Switch to Vuetify theme +setTooltipGlobalTheme('default') // Revert to default styles +``` + +### Customizing Theme Styles + +Each theme uses CSS custom properties (variables) for easy customization. You can override these in your global CSS: + +```css +:root { + /* Example for PrimeVue theme */ + --vct-primevue-background: #1a1a1a; + --vct-primevue-text-color: #fff; + --vct-primevue-border-radius: 8px; +} +``` + +See the [src/styles/themes/README.md](src/styles/themes/README.md) for a full list of theme variables and instructions for creating your own custom themes. + ## Styling The tooltip uses CSS custom properties for theming. You can customize the appearance by overriding these variables: diff --git a/THEME_GUIDE.md b/THEME_GUIDE.md new file mode 100644 index 0000000..a2eeda5 --- /dev/null +++ b/THEME_GUIDE.md @@ -0,0 +1,139 @@ +# Theme Configuration Guide + +## Overview + +Vue Custom Tooltip supports predefined UI framework themes! You can easily apply PrimeVue or Vuetify styling to all your tooltips with a simple configuration option. + +## Available Themes + +- **`default`**: The built-in theme using the component's original styles (no additional CSS loaded) +- **`primevue`**: Styles inspired by PrimeVue's design system +- **`vuetify`**: Styles inspired by Vuetify's Material Design implementation + +## Basic Usage + +### Option 1: Global Theme Configuration + +Apply a theme to all tooltips in your application: + +```typescript +import { createApp } from 'vue' +import { VueCustomTooltip } from 'vue-custom-tooltip' +import App from './App.vue' + +const app = createApp(App) + +app.use(VueCustomTooltip, { + theme: 'primevue' // or 'vuetify' or 'default' +}) + +app.mount('#app') +``` + +### Option 2: Theme with Global Config + +Combine theme styling with global configuration: + +```typescript +app.use(VueCustomTooltip, { + theme: 'primevue', + globalConfig: { + position: 'top', + trigger: 'hover', + showDelay: 200, + hideDelay: 150, + dark: 'auto', // Supports auto-detection, true, or false + showArrow: true, + offset: 12, + maxWidth: '300px', + }, +}) +``` + +### Option 3: No Theme (Default Styling) + +If you don't specify a theme, the default tooltip styling will be used: + +```typescript +// These are equivalent +app.use(VueCustomTooltip) +app.use(VueCustomTooltip, { theme: 'default' }) +``` + +## Programmatic Theme Control + +You can also change the theme programmatically: + +```typescript +import { getTooltipGlobalTheme, setTooltipGlobalTheme } from 'vue-custom-tooltip' + +// Change theme at runtime +setTooltipGlobalTheme('vuetify') + +// Get current theme +const currentTheme = getTooltipGlobalTheme() + +// Revert to default (uses component's built-in styles) +setTooltipGlobalTheme('default') +// or +setTooltipGlobalTheme(undefined) +``` + +## Theme Features + +### Dark Mode Support + +All themes automatically support dark mode through: +1. **Auto detection** (`dark: 'auto'`): Responds to Tailwind's `.dark` class or `prefers-color-scheme` +2. **Forced dark mode** (`dark: true`): Always use dark theme +3. **Forced light mode** (`dark: false`): Always use light theme + +```typescript +// Auto-detect dark mode +app.use(VueCustomTooltip, { + theme: 'primevue', + globalConfig: { + dark: 'auto', // Default + }, +}) + +// Force dark mode +app.use(VueCustomTooltip, { + theme: 'vuetify', + globalConfig: { + dark: true, + }, +}) +``` + +### CSS Custom Properties + +Each theme uses CSS custom properties that you can override in your own styles: + +```css +/* Override PrimeVue theme colors */ +:root { + --vct-primevue-background: #1a1a1a; + --vct-primevue-text-color: #ffffff; + --vct-primevue-border-radius: 8px; +} + +/* Override Vuetify theme colors */ +:root { + --vct-vuetify-background: rgba(50, 50, 50, 0.95); + --vct-vuetify-text-color: #e0e0e0; + --vct-vuetify-font-family: 'Custom Font', sans-serif; +} +``` + +## Notes + +- Themes are injected as ` diff --git a/src/components/tooltip/Tooltip.vue b/src/components/tooltip/Tooltip.vue index ed4a25e..88c0bdd 100644 --- a/src/components/tooltip/Tooltip.vue +++ b/src/components/tooltip/Tooltip.vue @@ -50,6 +50,7 @@ import { useTooltipProps, useTooltipVisibility, } from '../../composables' +import { getTooltipGlobalThemeRef } from '../../config/index' /** * Generic Tooltip Component @@ -159,6 +160,9 @@ const { // Computed properties const hasContentSlot = computed(() => !!slots.content) +// Get global theme +const globalTheme = getTooltipGlobalThemeRef() + const tooltipClasses = computed(() => [ 'custom-tooltip', `tooltip-${actualPosition.value}`, @@ -169,6 +173,8 @@ const tooltipClasses = computed(() => [ 'tooltip-light': effectiveDark.value === false, 'tooltip-auto': effectiveDark.value === 'auto', }, + // Only apply theme class if it's not 'default' (default uses component's built-in styles) + globalTheme.value && globalTheme.value !== 'default' ? `tooltip-theme-${globalTheme.value}` : '', effectiveTooltipClass.value, ]) diff --git a/src/config/globalConfig.ts b/src/config/globalConfig.ts index ac36fc5..1bbe4a7 100644 --- a/src/config/globalConfig.ts +++ b/src/config/globalConfig.ts @@ -1,12 +1,18 @@ -import type { TooltipProps } from '@/components/tooltip/Tooltip.vue' +import type { TooltipProps, TooltipTheme } from '@/types/tooltip' -import { reactive } from 'vue' +import { reactive, ref } from 'vue' +import { injectThemeStyles } from '../index' /** * Global reactive configuration for tooltips */ const globalConfig = reactive>({}) +/** + * Global theme configuration (stored separately from props) + */ +const globalTheme = ref(undefined) + /** * Set global configuration for all tooltips * This will be used as default values that can be overridden by individual tooltip props @@ -20,6 +26,28 @@ export function setTooltipGlobalConfig(config: Partial): void { Object.assign(globalConfig, config) } +/** + * Set the global theme for all tooltips + */ +export async function setTooltipGlobalTheme(theme: TooltipTheme | undefined): Promise { + globalTheme.value = theme + await injectThemeStyles(theme || 'default') +} + +/** + * Get the current global theme + */ +export function getTooltipGlobalTheme(): TooltipTheme | undefined { + return globalTheme.value +} + +/** + * Get the current global theme (reactive reference) + */ +export function getTooltipGlobalThemeRef() { + return globalTheme +} + /** * Get the current global configuration * Returns a copy to prevent external mutation @@ -44,4 +72,5 @@ export function resetTooltipGlobalConfig(): void { Object.keys(globalConfig).forEach((key) => { delete globalConfig[key as keyof TooltipProps] }) + globalTheme.value = undefined } diff --git a/src/config/index.ts b/src/config/index.ts index 664e2d4..6e35b40 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -2,6 +2,9 @@ export { getReactiveGlobalConfig, getTooltipGlobalConfig, + getTooltipGlobalTheme, + getTooltipGlobalThemeRef, resetTooltipGlobalConfig, setTooltipGlobalConfig, + setTooltipGlobalTheme, } from './globalConfig' diff --git a/src/index.ts b/src/index.ts index 77b89ce..c8c7017 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import type { App, Plugin } from 'vue' -import type { TooltipProps } from './types/tooltip' +import type { TooltipProps, TooltipTheme } from './types/tooltip' import Tooltip from './components/tooltip/Tooltip.vue' -import { setTooltipGlobalConfig } from './config/index' +import { setTooltipGlobalConfig, setTooltipGlobalTheme } from './config/index' import { vTooltip } from './directives/tooltip' // Export components and directives @@ -12,9 +12,9 @@ export { Tooltip, vTooltip } export * from './composables' // Export configuration functions -export { getTooltipGlobalConfig, resetTooltipGlobalConfig, setTooltipGlobalConfig } from './config/index' +export { getTooltipGlobalConfig, getTooltipGlobalTheme, resetTooltipGlobalConfig, setTooltipGlobalConfig, setTooltipGlobalTheme } from './config/index' -export type { TooltipProps, TooltipSlots } from './types/tooltip' +export type { TooltipProps, TooltipSlots, TooltipTheme } from './types/tooltip' // Export types export type { TooltipDirectiveModifiers, @@ -34,8 +34,63 @@ export interface VueCustomTooltipOptions { * These values will be used as defaults and can be overridden by individual tooltip props */ globalConfig?: Partial + /** + * Theme to apply to all tooltips + * Available themes: 'primevue', 'vuetify' + * If not specified, the default theme will be used + */ + theme?: TooltipTheme +} + +/** + * Injects theme styles into the document head + */ +async function injectThemeStyles(theme: TooltipTheme): Promise { + // Default theme uses the component's built-in styles, no CSS injection needed + if (theme === 'default') { + // Remove any previously injected theme styles to revert to default + const oldStyles = document.querySelectorAll('style[data-vct-theme]') + oldStyles.forEach(style => style.remove()) + return + } + + // Check if theme styles are already injected + const existingStyle = document.querySelector(`style[data-vct-theme="${theme}"]`) + if (existingStyle) { + return + } + + // Remove any previously injected theme styles + const oldStyles = document.querySelectorAll('style[data-vct-theme]') + oldStyles.forEach(style => style.remove()) + + try { + // Import the theme CSS dynamically + if (theme === 'primevue') { + await import('./styles/themes/primevue.css') + } + else if (theme === 'vuetify') { + await import('./styles/themes/vuetify.css') + } + else { + console.warn(`Unknown theme "${theme}"`) + return + } + + // Mark that this theme has been loaded + const marker = document.createElement('style') + marker.setAttribute('data-vct-theme', theme) + marker.textContent = `/* Vue Custom Tooltip Theme: ${theme} */` + document.head.appendChild(marker) + } + catch (error) { + console.error(`Failed to load theme "${theme}":`, error) + } } +// Export theme CSS injector for runtime theme switching +export { injectThemeStyles } + // Vue plugin for easy installation export const VueCustomTooltip: Plugin = { install(app: App, options?: VueCustomTooltipOptions) { @@ -46,6 +101,15 @@ export const VueCustomTooltip: Plugin = { if (options?.globalConfig) { setTooltipGlobalConfig(options.globalConfig) } + + // Apply theme if provided + if (options?.theme) { + setTooltipGlobalTheme(options.theme) + // Inject theme styles + if (typeof document !== 'undefined') { + injectThemeStyles(options.theme) + } + } }, } diff --git a/src/main.ts b/src/main.ts index ad0e965..e0814b7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,23 +18,19 @@ import './assets/main.css' const app = createApp(App) -app.use(VueCustomTooltip) - -// app.directive('tooltip', vTooltip) -// app.component('Tooltip', Tooltip) - // Different way to set global configuration -// app.use(VueCustomTooltip, { -// globalConfig: { -// position: 'top', // Default position for all tooltips -// trigger: 'hover', // Default trigger behavior -// showDelay: 200, // Default show delay (ms) -// hideDelay: 150, // Default hide delay (ms) -// dark: false, // Force dark mode for all tooltips -// showArrow: true, // Show arrow by default -// offset: 12, // Default offset from trigger -// maxWidth: '300px', // Default max width -// }, -// }) +app.use(VueCustomTooltip, { + theme: 'default', // Apply Default theme to all tooltips - 'default', 'primevue' or 'vuetify' + // globalConfig: { + // position: 'top', // Default position for all tooltips + // trigger: 'hover', // Default trigger behavior + // showDelay: 200, // Default show delay (ms) + // hideDelay: 150, // Default hide delay (ms) + // dark: 'auto', // Auto detect dark mode + // showArrow: true, // Show arrow by default + // offset: 12, // Default offset from trigger + // maxWidth: '300px', // Default max width + // }, +}) app.mount('#app') diff --git a/src/styles/themes/README.md b/src/styles/themes/README.md new file mode 100644 index 0000000..86d5192 --- /dev/null +++ b/src/styles/themes/README.md @@ -0,0 +1,120 @@ +# Tooltip Themes + +This directory contains theme-specific styles for the Vue Custom Tooltip component. + +## Available Themes + +### Default (`default.css`) +The built-in theme that uses the component's original styles. This file is intentionally empty as the default styles are defined in the `Tooltip.vue` component itself. + +**Features:** +- Light, clean design +- Subtle shadows and borders +- Automatic dark mode support +- No additional CSS loading required + +**When to use:** +- When you want the original tooltip styling +- To explicitly revert from another theme +- As a fallback when no theme is specified + +**Note:** Using `theme: 'default'` is equivalent to not specifying a theme at all. + +### PrimeVue (`primevue.css`) +Styles inspired by PrimeVue's design system, featuring: +- Clean, modern appearance +- Material-inspired shadows +- Subtle border radius +- Dark background with high contrast text +- Smooth transitions + +### Vuetify (`vuetify.css`) +Styles inspired by Vuetify's Material Design implementation, featuring: +- Material Design specifications +- Elevated shadows +- Roboto font family +- Semi-transparent backgrounds +- Precise spacing and typography + +## Usage + +Themes are automatically injected when you configure the plugin: + +```typescript +import { createApp } from 'vue' +import { VueCustomTooltip } from 'vue-custom-tooltip' + +const app = createApp(App) + +// Use a specific theme +app.use(VueCustomTooltip, { + theme: 'primevue' // or 'vuetify' or 'default' +}) + +// No theme (same as theme: 'default') +app.use(VueCustomTooltip) +``` + +## Customization + +Each theme uses CSS custom properties (variables) that you can override: + +### PrimeVue Theme Variables +```css +--vct-primevue-text-color +--vct-primevue-background +--vct-primevue-border-radius +--vct-primevue-font-family +--vct-primevue-text-color-light +--vct-primevue-background-light +--vct-primevue-text-color-dark +--vct-primevue-background-dark +``` + +### Vuetify Theme Variables +```css +--vct-vuetify-text-color +--vct-vuetify-background +--vct-vuetify-border-radius +--vct-vuetify-font-family +--vct-vuetify-text-color-light +--vct-vuetify-background-light +--vct-vuetify-text-color-dark +--vct-vuetify-background-dark +``` + +## Dark Mode Support + +All themes support: +- Forced dark mode (`dark: true`) +- Forced light mode (`dark: false`) +- Auto detection (`dark: 'auto'`) - responds to: + - Tailwind's `.dark` class + - `prefers-color-scheme` media query + +## Creating Custom Themes + +To create a custom theme: + +1. Create a new CSS file in this directory (e.g., `mytheme.css`) +2. Use the class selector `.tooltip-theme-mytheme` +3. Style the `.tooltip-content` and `.tooltip-arrow` elements +4. Support all dark mode variants +5. Add the theme name to the `TooltipTheme` type in `src/types/tooltip.ts` +6. Update the theme injection logic in `src/index.ts` + +Example structure: +```css +.tooltip-theme-mytheme .tooltip-content { + /* Your styles */ +} + +.tooltip-theme-mytheme .tooltip-arrow { + /* Your styles */ +} + +/* Dark mode support */ +.tooltip-theme-mytheme.tooltip-dark .tooltip-content { + /* Dark mode styles */ +} +``` diff --git a/src/styles/themes/default.css b/src/styles/themes/default.css new file mode 100644 index 0000000..4e750fa --- /dev/null +++ b/src/styles/themes/default.css @@ -0,0 +1,14 @@ +/** + * Default Theme for Vue Custom Tooltip + * + * This file is intentionally empty because the default theme uses the + * built-in styles from the Tooltip.vue component's