You've already forked php-flasher
mirror of
https://github.com/php-flasher/php-flasher.git
synced 2026-04-05 12:32:55 +01:00
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* @file PHPFlasher Google Theme Styles
|
||||
* @description CSS styling for Material Design-inspired notifications
|
||||
* @author yoeunes
|
||||
*/
|
||||
|
||||
/**
|
||||
* PHPFlasher - Google Theme
|
||||
*
|
||||
* Google's Material Design-inspired notifications.
|
||||
* One of the most recognized design systems worldwide.
|
||||
*/
|
||||
.fl-google {
|
||||
/* Theme variables - Define the visual appearance of Material Design notifications */
|
||||
|
||||
/* Base colors and appearance for light and dark modes */
|
||||
--md-bg-light: #ffffff; /* Card background in light mode */
|
||||
--md-bg-dark: #2d2d2d; /* Card background in dark mode */
|
||||
--md-text-light: rgba(0, 0, 0, 0.87); /* Primary text in light mode (87% black) */
|
||||
--md-text-secondary-light: rgba(0, 0, 0, 0.6); /* Secondary text in light mode (60% black) */
|
||||
--md-text-dark: rgba(255, 255, 255, 0.87); /* Primary text in dark mode (87% white) */
|
||||
--md-text-secondary-dark: rgba(255, 255, 255, 0.6); /* Secondary text in dark mode (60% white) */
|
||||
|
||||
/* Material Design elevation - multi-layered shadows */
|
||||
--md-elevation: 0 3px 5px -1px rgba(0,0,0,0.2), /* Ambient shadow */
|
||||
0 6px 10px 0 rgba(0,0,0,0.14), /* Penumbra shadow */
|
||||
0 1px 18px 0 rgba(0,0,0,0.12); /* Umbra shadow */
|
||||
|
||||
--md-elevation-dark: 0 3px 5px -1px rgba(0,0,0,0.4), /* Darker ambient shadow */
|
||||
0 6px 10px 0 rgba(0,0,0,0.28), /* Darker penumbra shadow */
|
||||
0 1px 18px 0 rgba(0,0,0,0.24); /* Darker umbra shadow */
|
||||
--md-border-radius: 4px; /* Material Design default corner radius */
|
||||
|
||||
/* Material Design color palette for notification types */
|
||||
--md-success: #43a047; /* Green 600 from Material palette */
|
||||
--md-info: #1e88e5; /* Blue 600 from Material palette */
|
||||
--md-warning: #fb8c00; /* Orange 600 from Material palette */
|
||||
--md-error: #e53935; /* Red 600 from Material palette */
|
||||
|
||||
/* Animation timing variables */
|
||||
--md-animation-duration: 0.3s; /* Standard Material motion duration */
|
||||
--md-ripple-duration: 0.6s; /* Ripple effect duration */
|
||||
}
|
||||
|
||||
/**
|
||||
* Entrance animation for Google theme
|
||||
* Material Design-style slide up with easing
|
||||
*/
|
||||
@keyframes mdSlideUp {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(20px); /* Start below final position */
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0); /* End at final position */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Material Design ripple effect animation
|
||||
* Creates expanding circle from point of interaction
|
||||
*/
|
||||
@keyframes mdRipple {
|
||||
to {
|
||||
transform: scale(4); /* Expand to 4x original size */
|
||||
opacity: 0; /* Fade out completely */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base Google Material Design theme styling
|
||||
*/
|
||||
.fl-google {
|
||||
position: relative;
|
||||
margin: 8px 0; /* Spacing between notifications */
|
||||
width: 100%;
|
||||
max-width: 400px; /* Maximum width following Material guidelines */
|
||||
font-family: Roboto, "Segoe UI", Helvetica, Arial, sans-serif; /* Material uses Roboto */
|
||||
animation: mdSlideUp var(--md-animation-duration) cubic-bezier(0.4, 0, 0.2, 1); /* Material standard easing */
|
||||
|
||||
/**
|
||||
* Main card container
|
||||
* Follows Material Design card component styling
|
||||
*/
|
||||
.fl-md-card {
|
||||
background-color: var(--md-bg-light);
|
||||
color: var(--md-text-light);
|
||||
border-radius: var(--md-border-radius);
|
||||
box-shadow: var(--md-elevation); /* Multi-layered shadow for depth */
|
||||
overflow: hidden; /* Contains progress bar */
|
||||
}
|
||||
|
||||
/**
|
||||
* Content section
|
||||
* Contains icon and text content
|
||||
*/
|
||||
.fl-content {
|
||||
padding: 16px; /* Standard Material padding */
|
||||
display: flex;
|
||||
align-items: flex-start; /* Align items to top */
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon container
|
||||
* Holds the Material Design SVG icon
|
||||
*/
|
||||
.fl-icon-wrapper {
|
||||
margin-right: 16px; /* Space between icon and text */
|
||||
color: var(--md-text-secondary-light); /* Default icon color */
|
||||
flex-shrink: 0; /* Prevent icon from shrinking */
|
||||
}
|
||||
|
||||
/**
|
||||
* Text content container
|
||||
* Holds title and message
|
||||
*/
|
||||
.fl-text-content {
|
||||
flex: 1; /* Take available space */
|
||||
}
|
||||
|
||||
/**
|
||||
* Title styling
|
||||
* Following Material Design typography
|
||||
*/
|
||||
.fl-title {
|
||||
font-size: 1rem; /* 16px - Material body 1 */
|
||||
font-weight: 500; /* Medium weight per Material guidelines */
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message styling
|
||||
* Following Material Design typography
|
||||
*/
|
||||
.fl-message {
|
||||
font-size: 0.875rem; /* 14px - Material body 2 */
|
||||
line-height: 1.43; /* Material line height for body 2 */
|
||||
color: var(--md-text-secondary-light); /* Secondary text color (60% opacity) */
|
||||
}
|
||||
|
||||
/**
|
||||
* Action buttons section
|
||||
* Contains dismiss button
|
||||
*/
|
||||
.fl-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end; /* Align buttons to right */
|
||||
padding: 8px; /* Standard Material padding */
|
||||
}
|
||||
|
||||
/**
|
||||
* Action button styling
|
||||
* Following Material Design "text button" guidelines
|
||||
*/
|
||||
.fl-action-button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: currentColor; /* Inherit color from parent */
|
||||
font-family: inherit;
|
||||
font-weight: 500; /* Medium weight per Material specs */
|
||||
font-size: 0.8125rem; /* 13px - Material button text size */
|
||||
text-transform: uppercase; /* Material buttons use uppercase */
|
||||
letter-spacing: 0.0892857143em; /* Material's letter spacing for buttons */
|
||||
padding: 8px 12px; /* Material button padding */
|
||||
border-radius: 4px; /* Rounded corners per Material */
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s; /* Smooth hover effect */
|
||||
position: relative; /* For ripple positioning */
|
||||
overflow: hidden; /* Contain ripple effect */
|
||||
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(0, 0, 0, 0.04); /* Material hover state - 4% opacity */
|
||||
}
|
||||
|
||||
/**
|
||||
* Ripple effect
|
||||
* Material Design's signature ink ripple on interaction
|
||||
*/
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 5px; /* Initial small circle */
|
||||
height: 5px;
|
||||
background: currentColor; /* Use button text color */
|
||||
opacity: 0;
|
||||
border-radius: 50%; /* Perfect circle */
|
||||
transform: scale(1);
|
||||
pointer-events: none; /* Don't interfere with clicks */
|
||||
}
|
||||
|
||||
&:active::after {
|
||||
opacity: 0.3; /* Material ripple opacity */
|
||||
animation: mdRipple var(--md-ripple-duration) linear; /* Expand and fade */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-specific styling
|
||||
* Each notification type has its own color based on Material palette
|
||||
*/
|
||||
&.fl-success {
|
||||
.fl-icon-wrapper {
|
||||
color: var(--md-success); /* Green icon */
|
||||
}
|
||||
|
||||
.fl-action-button {
|
||||
color: var(--md-success); /* Green button */
|
||||
}
|
||||
}
|
||||
|
||||
&.fl-info {
|
||||
.fl-icon-wrapper {
|
||||
color: var(--md-info); /* Blue icon */
|
||||
}
|
||||
|
||||
.fl-action-button {
|
||||
color: var(--md-info); /* Blue button */
|
||||
}
|
||||
}
|
||||
|
||||
&.fl-warning {
|
||||
.fl-icon-wrapper {
|
||||
color: var(--md-warning); /* Orange icon */
|
||||
}
|
||||
|
||||
.fl-action-button {
|
||||
color: var(--md-warning); /* Orange button */
|
||||
}
|
||||
}
|
||||
|
||||
&.fl-error {
|
||||
.fl-icon-wrapper {
|
||||
color: var(--md-error); /* Red icon */
|
||||
}
|
||||
|
||||
.fl-action-button {
|
||||
color: var(--md-error); /* Red button */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Progress bar
|
||||
* Material Design linear progress indicator
|
||||
*/
|
||||
.fl-progress-bar {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px; /* Material's standard progress height */
|
||||
overflow: hidden;
|
||||
|
||||
.fl-progress {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transform-origin: left center; /* Animation starts from left */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-specific progress colors
|
||||
* Each notification type has its own progress bar color
|
||||
*/
|
||||
&.fl-success .fl-progress {
|
||||
background-color: var(--md-success); /* Green progress */
|
||||
}
|
||||
|
||||
&.fl-info .fl-progress {
|
||||
background-color: var(--md-info); /* Blue progress */
|
||||
}
|
||||
|
||||
&.fl-warning .fl-progress {
|
||||
background-color: var(--md-warning); /* Orange progress */
|
||||
}
|
||||
|
||||
&.fl-error .fl-progress {
|
||||
background-color: var(--md-error); /* Red progress */
|
||||
}
|
||||
|
||||
/**
|
||||
* RTL Support
|
||||
* Right-to-left language direction support
|
||||
*/
|
||||
&.fl-rtl {
|
||||
direction: rtl;
|
||||
|
||||
.fl-content {
|
||||
flex-direction: row-reverse; /* Reverse flex direction */
|
||||
}
|
||||
|
||||
.fl-icon-wrapper {
|
||||
margin-right: 0;
|
||||
margin-left: 16px; /* Swap margins for RTL */
|
||||
}
|
||||
|
||||
.fl-actions {
|
||||
justify-content: flex-start; /* Align buttons to left in RTL */
|
||||
}
|
||||
|
||||
.fl-progress {
|
||||
transform-origin: right center; /* Animation starts from right */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessibility
|
||||
* Respects reduced motion preferences
|
||||
*/
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
animation: none; /* Disable slide animation */
|
||||
|
||||
.fl-action-button:active::after {
|
||||
animation: none; /* Disable ripple animation */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dark mode support
|
||||
* Material Design dark theme implementation
|
||||
*/
|
||||
body.fl-dark .fl-google,
|
||||
html.fl-dark .fl-google,
|
||||
.fl-google.fl-auto-dark {
|
||||
.fl-md-card {
|
||||
background-color: var(--md-bg-dark); /* Darker background */
|
||||
color: var(--md-text-dark); /* Lighter text */
|
||||
box-shadow: var(--md-elevation-dark); /* Stronger shadows */
|
||||
}
|
||||
|
||||
.fl-message {
|
||||
color: var(--md-text-secondary-dark); /* Lighter secondary text */
|
||||
}
|
||||
|
||||
.fl-action-button {
|
||||
&:hover, &:focus {
|
||||
background-color: rgba(255, 255, 255, 0.08); /* Material dark hover - 8% opacity */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @file PHPFlasher Google Theme Implementation
|
||||
* @description Material Design-inspired notification theme
|
||||
* @author yoeunes
|
||||
*/
|
||||
import './google.scss'
|
||||
import type { Envelope } from '../../types'
|
||||
|
||||
/**
|
||||
* Google Material Design-inspired notification theme for PHPFlasher.
|
||||
*
|
||||
* This theme replicates Google's Material Design aesthetics with:
|
||||
* - Elevated card component with proper shadow depth
|
||||
* - Material Design iconography
|
||||
* - Google's typography system with Roboto font
|
||||
* - Material "ink ripple" effect on buttons
|
||||
* - UPPERCASE action buttons following Material guidelines
|
||||
* - Smooth animation with Material Design's motion patterns
|
||||
*
|
||||
* The theme follows Material Design specifications for components like
|
||||
* cards, buttons, typography and iconography, creating a familiar experience
|
||||
* for users of Google products.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import flasher from '@flasher/flasher';
|
||||
* import { googleTheme } from '@flasher/flasher/themes';
|
||||
*
|
||||
* // Register the theme (if not already registered)
|
||||
* flasher.addTheme('google', googleTheme);
|
||||
*
|
||||
* // Use the theme
|
||||
* flasher.use('theme.google').success('Operation completed successfully');
|
||||
* ```
|
||||
*/
|
||||
export const googleTheme = {
|
||||
/**
|
||||
* Renders a notification envelope as HTML.
|
||||
*
|
||||
* Creates a Google Material Design-style notification with icon, title (optional),
|
||||
* message, dismissal button, and progress indicator.
|
||||
*
|
||||
* @param envelope - The notification envelope to render
|
||||
* @returns HTML string representation of the notification
|
||||
*/
|
||||
render: (envelope: Envelope): string => {
|
||||
const { type, message, title } = envelope
|
||||
|
||||
// Set appropriate ARIA roles based on notification type
|
||||
const isAlert = type === 'error' || type === 'warning'
|
||||
const role = isAlert ? 'alert' : 'status'
|
||||
const ariaLive = isAlert ? 'assertive' : 'polite'
|
||||
|
||||
// Action button text in Material Design style (uppercase)
|
||||
const actionText = 'DISMISS'
|
||||
|
||||
/**
|
||||
* Gets the appropriate Material Design icon based on notification type.
|
||||
* Each icon follows Google's Material Design iconography guidelines.
|
||||
*
|
||||
* @returns SVG markup for the notification icon
|
||||
*/
|
||||
const getIcon = () => {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
return `<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
|
||||
</svg>`
|
||||
case 'error':
|
||||
return `<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M12 2C6.47 2 2 6.47 2 12s4.47 10 10 10 10-4.47 10-10S17.53 2 12 2zm5 13.59L15.59 17 12 13.41 8.41 17 7 15.59 10.59 12 7 8.41 8.41 7 12 10.59 15.59 7 17 8.41 13.41 12 17 15.59z"/>
|
||||
</svg>`
|
||||
case 'warning':
|
||||
return `<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M12 5.99L19.53 19H4.47L12 5.99M12 2L1 21h22L12 2zm1 14h-2v2h2v-2zm0-6h-2v4h2v-4z"/>
|
||||
</svg>`
|
||||
case 'info':
|
||||
return `<svg class="fl-icon-svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
|
||||
</svg>`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// Generate title section if title is provided
|
||||
const titleSection = title ? `<div class="fl-title">${title}</div>` : ''
|
||||
|
||||
return `
|
||||
<div class="fl-google fl-${type}" role="${role}" aria-live="${ariaLive}" aria-atomic="true">
|
||||
<div class="fl-md-card">
|
||||
<div class="fl-content">
|
||||
<div class="fl-icon-wrapper">
|
||||
${getIcon()}
|
||||
</div>
|
||||
<div class="fl-text-content">
|
||||
${titleSection}
|
||||
<div class="fl-message">${message}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fl-actions">
|
||||
<button class="fl-action-button fl-close" aria-label="Close ${type} message">
|
||||
${actionText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fl-progress-bar">
|
||||
<div class="fl-progress"></div>
|
||||
</div>
|
||||
</div>`
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file PHPFlasher Google Theme Registration
|
||||
* @description Registers the Google theme with PHPFlasher
|
||||
* @author yoeunes
|
||||
*/
|
||||
import flasher from '../../index'
|
||||
import { googleTheme } from './google'
|
||||
|
||||
/**
|
||||
* Register the Google theme.
|
||||
*
|
||||
* This theme provides notifications styled after Google's Material Design system.
|
||||
* The registration makes the theme available under the name 'google'.
|
||||
*
|
||||
* The Google theme can be used by calling:
|
||||
* ```typescript
|
||||
* flasher.use('theme.google').success('Operation completed successfully');
|
||||
* ```
|
||||
*
|
||||
* Key features:
|
||||
* - Material Design card with proper elevation
|
||||
* - Google's typography system with Roboto font
|
||||
* - Material Design icons and color palette
|
||||
* - Ripple effect on interaction
|
||||
* - UPPERCASE action button text
|
||||
* - Proper implementation of Material Design light and dark themes
|
||||
*/
|
||||
flasher.addTheme('google', googleTheme)
|
||||
Reference in New Issue
Block a user