Skip to content

Commit

Permalink
Added ripple
Browse files Browse the repository at this point in the history
  • Loading branch information
VampireAotD committed Nov 2, 2024
1 parent 807339d commit c235e0a
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/resources/js/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createInertiaApp } from '@inertiajs/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { ZiggyVue } from 'ziggy-js';

import ripple from '@/shared/directives/ripple';
import { HasRolePlugin } from '@/shared/plugins/user/authorize';

import '../css/app.css';
Expand All @@ -23,6 +24,7 @@ createInertiaApp({
.use(plugin)
.use(ZiggyVue)
.use(HasRolePlugin)
.directive('ripple', ripple)
.mount(el);
},
progress: {
Expand Down
2 changes: 1 addition & 1 deletion src/resources/js/features/theme-switcher/ThemeSwitcher.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const toggleDarkMode = useToggle(isDark);
</script>

<template>
<Button variant="outline" size="rounded" @click="toggleDarkMode()">
<Button v-ripple variant="outline" size="rounded" @click="toggleDarkMode()">
<Sun v-if="isDark" />
<Moon v-else />
</Button>
Expand Down
83 changes: 83 additions & 0 deletions src/resources/js/shared/directives/ripple.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';

import RippleDirective from './ripple';

describe('Ripple directive test (ripple.ts)', () => {
const createDirectiveWrapper = (bindingValue = {}) => {
return mount({
template: `
<div v-ripple="options" style="width: 100px; height: 100px;"></div>`,
data() {
return { options: bindingValue };
},
directives: {
ripple: RippleDirective,
},
});
};

it('Will apply default classes when no options are provided', async () => {
const wrapper = createDirectiveWrapper();
await wrapper.trigger('click', {
clientX: 50,
clientY: 50,
});

const rippleElement = wrapper.element.querySelector('span');
expect(rippleElement).toBeTruthy();
expect(rippleElement.classList.contains('animate-ripple')).toBeTruthy();
expect(rippleElement.classList.contains('bg-foreground')).toBeTruthy();
expect(rippleElement.classList.contains('bg-opacity-25')).toBeTruthy();
});

it('Will apply provided color and opacity classes', async () => {
const wrapper = createDirectiveWrapper({
color: 'bg-blue-500',
opacity: 'bg-opacity-50',
});

await wrapper.trigger('click', {
clientX: 50,
clientY: 50,
});

const rippleElement = wrapper.element.querySelector('span');
expect(rippleElement).toBeTruthy();
expect(rippleElement.classList.contains('animate-ripple')).toBeTruthy();
expect(rippleElement.classList.contains('bg-blue-500')).toBeTruthy();
expect(rippleElement.classList.contains('bg-opacity-50')).toBeTruthy();
});

it('Will position the ripple correctly', async () => {
const wrapper = createDirectiveWrapper();
const rect = wrapper.element.getBoundingClientRect();
const clickX = rect.left + rect.width / 2;
const clickY = rect.top + rect.height / 2;

await wrapper.trigger('click', {
clientX: clickX,
clientY: clickY,
});

const rippleElement = wrapper.element.querySelector('span');
expect(rippleElement).toBeTruthy();
expect(rippleElement.style.left).toBe(`${clickX - rect.left - rect.width / 2}px`);
expect(rippleElement.style.top).toBe(`${clickY - rect.top - rect.height / 2}px`);
});

it('Will remove ripple after animation ends', async () => {
const wrapper = createDirectiveWrapper();
await wrapper.trigger('click', {
clientX: 50,
clientY: 50,
});

let rippleElement = wrapper.element.querySelector('span');
expect(rippleElement).toBeTruthy();

rippleElement.dispatchEvent(new Event('animationend'));
rippleElement = wrapper.element.querySelector('span');
expect(rippleElement).toBeFalsy();
});
});
42 changes: 42 additions & 0 deletions src/resources/js/shared/directives/ripple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { DirectiveBinding } from 'vue';

type RippleOptions = {
color?: string;
opacity?: string;
};

export default {
mounted(el: HTMLElement, binding: DirectiveBinding<RippleOptions>) {
el.style.position = 'relative';
el.style.overflow = 'hidden';

el.addEventListener('click', function (e: MouseEvent) {
const ripple = document.createElement('span');
const rect = el.getBoundingClientRect();

const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;

ripple.style.width = ripple.style.height = `${size}px`;
ripple.style.position = 'absolute';
ripple.style.borderRadius = '50%';
ripple.style.transform = 'scale(0)';
ripple.style.opacity = '1';
ripple.style.pointerEvents = 'none';
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;

const { color = 'bg-foreground', opacity = 'bg-opacity-25' } =
binding.value || {};

ripple.classList.add('animate-ripple', color, opacity);

el.appendChild(ripple);

ripple.addEventListener('animationend', () => {
ripple.remove();
});
});
},
};
8 changes: 6 additions & 2 deletions src/tailwind.config.js → src/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import forms from '@tailwindcss/forms';
import type { Config } from 'tailwindcss';
import animate from 'tailwindcss-animate';

/** @type {import('tailwindcss').Config} */
export default {
darkMode: ['class'],

Expand Down Expand Up @@ -79,15 +79,19 @@ export default {
from: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 0 },
},
'ripple-animation': {
to: { transform: 'scale(4)', opacity: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'collapsible-down': 'collapsible-down 0.2s ease-in-out',
'collapsible-up': 'collapsible-up 0.2s ease-in-out',
ripple: 'ripple-animation 0.6s linear',
},
},
},

plugins: [forms, animate],
};
} satisfies Config;

0 comments on commit c235e0a

Please sign in to comment.