mirror of
https://github.com/peritus/obsidian-interactive-ratings.git
synced 2026-07-22 05:43:17 +00:00
Add support for full-only rating symbols like 🎥
- Added fullOnly flag to SymbolSet interface - Added 10 new full-only symbols (🎥, 🏆, ⭐, 💎, 🔥, ⚡, 🎯, 🚀, 💰, 🎖️) - Visual: rated symbols full color, unrated grey 50% opacity - Disk: only rated symbols saved (e.g., 🎥🎥🎥 (3/5)) - No 0 ratings, no half ratings for full-only symbols - Updated CSS, tests, and documentation Refactor full-only symbols: auto-detect, show denominator count, require rating text - Remove fullOnly flag, auto-detect with isFullOnlySymbol() - Visual: show denominator count (numerator color, rest grey) - Require rating text for denominator info - Disk: save only rated symbols - Updated docs and tests Clarify and verify full-only symbol visual behavior - Added debug test files to verify expected behavior - Confirmed visual display always shows denominator count symbols - Rated symbols in full color, unrated in grey 50% opacity - Implementation already correct: displaySymbolCount uses denominator - Added debug logging capability for troubleshooting
This commit is contained in:
parent
014446d672
commit
1218efd3aa
4 changed files with 221 additions and 64 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { SymbolSet } from './types';
|
||||
|
||||
// Global logging control - set at build time via environment variable
|
||||
export const LOGGING_ENABLED = process.env.LOGGING_ENABLED === 'true';
|
||||
// Global logging control - set to false for production, true for debugging
|
||||
export const LOGGING_ENABLED = false;
|
||||
|
||||
// Define symbol patterns as a global constant
|
||||
export const SYMBOL_PATTERNS: SymbolSet[] = [
|
||||
|
|
@ -33,6 +33,18 @@ export const SYMBOL_PATTERNS: SymbolSet[] = [
|
|||
{ full: '⬤', empty: '◯', half: null }, // Bold circles
|
||||
{ full: '⚫', empty: '⚪', half: null }, // Black/white circles
|
||||
{ full: '█', empty: '░', half: null }, // Block/light shade
|
||||
|
||||
// Full-only symbols (same symbol for full and empty, no half)
|
||||
{ full: '🎥', empty: '🎥', half: null }, // Movie cameras
|
||||
{ full: '🏆', empty: '🏆', half: null }, // Trophies
|
||||
{ full: '⭐', empty: '⭐', half: null }, // Gold stars
|
||||
{ full: '💎', empty: '💎', half: null }, // Diamonds
|
||||
{ full: '🔥', empty: '🔥', half: null }, // Fire
|
||||
{ full: '⚡', empty: '⚡', half: null }, // Lightning
|
||||
{ full: '🎯', empty: '🎯', half: null }, // Target/bullseye
|
||||
{ full: '🚀', empty: '🚀', half: null }, // Rockets
|
||||
{ full: '💰', empty: '💰', half: null }, // Money bags
|
||||
{ full: '🎖️', empty: '🎖️', half: null }, // Military medals
|
||||
];
|
||||
|
||||
// Interaction constants
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { EditorView, Decoration, DecorationSet, ViewPlugin, ViewUpdate, WidgetType } from "@codemirror/view";
|
||||
import { RangeSetBuilder } from "@codemirror/state";
|
||||
import { generateSymbolRegexPatterns, getSymbolSetForPattern, calculateRating, parseRatingText } from './ratings-parser';
|
||||
import { generateSymbolsString, formatRatingText, getUnicodeCharLength, utf16ToUnicodePosition } from './utils';
|
||||
import { generateSymbolsString, generateSymbolsStringForDisk, formatRatingText, getUnicodeCharLength, utf16ToUnicodePosition, isFullOnlySymbol } from './utils';
|
||||
import { LOGGING_ENABLED } from './constants';
|
||||
import { RatingText } from './types';
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ interface RatingMatch {
|
|||
|
||||
/**
|
||||
* Rating widget for inline editing in CodeMirror
|
||||
* Handles both symbols and rating text with full half-symbol support
|
||||
* Handles both symbols and rating text with full half-symbol support and full-only symbols
|
||||
*/
|
||||
class RatingWidget extends WidgetType {
|
||||
constructor(
|
||||
|
|
@ -41,41 +41,48 @@ class RatingWidget extends WidgetType {
|
|||
container.setAttribute('data-pattern-length', unicodeLength.toString());
|
||||
container.setAttribute('data-supports-half', (!!this.symbolSet.half).toString());
|
||||
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
container.setAttribute('data-full-only', isFullOnly.toString());
|
||||
|
||||
// For full-only symbols, use denominator from rating text if available
|
||||
const displaySymbolCount = (isFullOnly && this.ratingText) ? this.ratingText.denominator : unicodeLength;
|
||||
container.setAttribute('data-display-symbol-count', displaySymbolCount.toString());
|
||||
|
||||
// Create symbols container
|
||||
const symbolsContainer = document.createElement('span');
|
||||
symbolsContainer.className = 'interactive-rating-symbols';
|
||||
|
||||
// Create clickable symbols
|
||||
[...this.pattern].forEach((symbol, index) => {
|
||||
// Create clickable symbols based on display count
|
||||
for (let i = 0; i < displaySymbolCount; i++) {
|
||||
const span = document.createElement('span');
|
||||
span.textContent = symbol;
|
||||
span.textContent = this.symbolSet.full;
|
||||
span.style.cursor = 'pointer';
|
||||
span.style.position = 'relative';
|
||||
span.setAttribute('data-symbol-index', index.toString());
|
||||
span.setAttribute('data-symbol-index', i.toString());
|
||||
|
||||
// Add click handler with half-symbol support
|
||||
// Add click handler with full-only validation
|
||||
span.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const newRating = this.calculateRatingFromClick(e, span, index);
|
||||
const newRating = this.calculateRatingFromClick(e, span, i);
|
||||
this.updateRating(view, newRating);
|
||||
});
|
||||
|
||||
// Add hover preview with half-symbol support
|
||||
// Add hover preview
|
||||
span.addEventListener('mouseenter', (e) => {
|
||||
const previewRating = this.calculateRatingFromHover(e, span, index);
|
||||
const previewRating = this.calculateRatingFromHover(e, span, i);
|
||||
this.previewRating(previewRating, container);
|
||||
});
|
||||
|
||||
// Add mousemove for fine-grained half-symbol preview
|
||||
// Add mousemove for fine-grained preview
|
||||
span.addEventListener('mousemove', (e) => {
|
||||
const previewRating = this.calculateRatingFromHover(e, span, index);
|
||||
const previewRating = this.calculateRatingFromHover(e, span, i);
|
||||
this.previewRating(previewRating, container);
|
||||
});
|
||||
|
||||
symbolsContainer.appendChild(span);
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(symbolsContainer);
|
||||
|
||||
|
|
@ -99,9 +106,16 @@ class RatingWidget extends WidgetType {
|
|||
}
|
||||
|
||||
/**
|
||||
* Calculate rating from click position with half-symbol support
|
||||
* Calculate rating from click position with full-only validation
|
||||
*/
|
||||
private calculateRatingFromClick(event: MouseEvent, span: HTMLElement, symbolIndex: number): number {
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
|
||||
// For full-only symbols, don't support half ratings and enforce minimum rating of 1
|
||||
if (isFullOnly) {
|
||||
return Math.max(1, symbolIndex + 1);
|
||||
}
|
||||
|
||||
if (!this.symbolSet.half) {
|
||||
// No half-symbol support, return full symbol rating
|
||||
return symbolIndex + 1;
|
||||
|
|
@ -121,9 +135,16 @@ class RatingWidget extends WidgetType {
|
|||
}
|
||||
|
||||
/**
|
||||
* Calculate rating from hover position with half-symbol support
|
||||
* Calculate rating from hover position with full-only validation
|
||||
*/
|
||||
private calculateRatingFromHover(event: MouseEvent, span: HTMLElement, symbolIndex: number): number {
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
|
||||
// For full-only symbols, don't support half ratings and enforce minimum rating of 1
|
||||
if (isFullOnly) {
|
||||
return Math.max(1, symbolIndex + 1);
|
||||
}
|
||||
|
||||
if (!this.symbolSet.half) {
|
||||
// No half-symbol support, return full symbol rating
|
||||
return symbolIndex + 1;
|
||||
|
|
@ -143,26 +164,47 @@ class RatingWidget extends WidgetType {
|
|||
}
|
||||
|
||||
/**
|
||||
* Preview rating with proper half-symbol rendering
|
||||
* Preview rating with proper half-symbol rendering and full-only symbol support
|
||||
*/
|
||||
private previewRating(newRating: number, container: HTMLElement): void {
|
||||
const symbolsContainer = container.querySelector('.interactive-rating-symbols');
|
||||
if (!symbolsContainer) return;
|
||||
|
||||
const spans = symbolsContainer.querySelectorAll('span');
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
|
||||
spans.forEach((span, index) => {
|
||||
const symbolRating = index + 1;
|
||||
const halfRating = index + 0.5;
|
||||
|
||||
if (symbolRating <= newRating) {
|
||||
// Full symbol
|
||||
if (isFullOnly) {
|
||||
// For full-only symbols: show full symbol for rated, grey for unrated
|
||||
span.textContent = this.symbolSet.full;
|
||||
} else if (this.symbolSet.half && halfRating <= newRating && halfRating > newRating - 0.5) {
|
||||
// Half symbol
|
||||
span.textContent = this.symbolSet.half;
|
||||
if (symbolRating <= newRating) {
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
} else {
|
||||
span.style.opacity = '0.5';
|
||||
span.style.filter = 'grayscale(100%)';
|
||||
}
|
||||
} else {
|
||||
// Empty symbol
|
||||
span.textContent = this.symbolSet.empty;
|
||||
// Regular symbol behavior
|
||||
if (symbolRating <= newRating) {
|
||||
// Full symbol
|
||||
span.textContent = this.symbolSet.full;
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
} else if (this.symbolSet.half && halfRating <= newRating && halfRating > newRating - 0.5) {
|
||||
// Half symbol
|
||||
span.textContent = this.symbolSet.half;
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
} else {
|
||||
// Empty symbol
|
||||
span.textContent = this.symbolSet.empty;
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -170,50 +212,67 @@ class RatingWidget extends WidgetType {
|
|||
if (this.ratingText) {
|
||||
const textContainer = container.querySelector('.interactive-rating-text');
|
||||
if (textContainer) {
|
||||
// Use the Unicode character count as the denominator for preview
|
||||
const unicodeLength = getUnicodeCharLength(this.pattern);
|
||||
const previewText = formatRatingText(
|
||||
this.ratingText.format,
|
||||
newRating,
|
||||
unicodeLength,
|
||||
unicodeLength, // Use symbol count as denominator
|
||||
!!this.symbolSet.half
|
||||
this.ratingText.denominator, // Use original denominator
|
||||
this.ratingText.denominator,
|
||||
!!this.symbolSet.half && !isFullOnly
|
||||
);
|
||||
textContainer.textContent = previewText;
|
||||
}
|
||||
}
|
||||
|
||||
if (LOGGING_ENABLED) {
|
||||
console.debug('[InteractiveRatings] Preview rating with half-symbol support', {
|
||||
console.debug('[InteractiveRatings] Preview rating with full-only support', {
|
||||
newRating,
|
||||
hasHalf: !!this.symbolSet.half,
|
||||
isFullOnly,
|
||||
symbolSet: this.symbolSet,
|
||||
denominator: getUnicodeCharLength(this.pattern)
|
||||
denominator: this.ratingText?.denominator
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render rating with proper half-symbol display
|
||||
* Render rating with proper half-symbol display and full-only symbol support
|
||||
*/
|
||||
private renderRating(rating: number, container: HTMLElement): void {
|
||||
const symbolsContainer = container.querySelector('.interactive-rating-symbols');
|
||||
if (!symbolsContainer) return;
|
||||
|
||||
const spans = symbolsContainer.querySelectorAll('span');
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
|
||||
spans.forEach((span, index) => {
|
||||
const symbolRating = index + 1;
|
||||
const halfRating = index + 0.5;
|
||||
|
||||
if (symbolRating <= rating) {
|
||||
// Full symbol
|
||||
if (isFullOnly) {
|
||||
// For full-only symbols: show full symbol for rated, grey for unrated
|
||||
span.textContent = this.symbolSet.full;
|
||||
} else if (this.symbolSet.half && halfRating <= rating && halfRating > rating - 0.5) {
|
||||
// Half symbol
|
||||
span.textContent = this.symbolSet.half;
|
||||
if (symbolRating <= rating) {
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
} else {
|
||||
span.style.opacity = '0.5';
|
||||
span.style.filter = 'grayscale(100%)';
|
||||
}
|
||||
} else {
|
||||
// Empty symbol
|
||||
span.textContent = this.symbolSet.empty;
|
||||
// Regular symbol behavior
|
||||
if (symbolRating <= rating) {
|
||||
// Full symbol
|
||||
span.textContent = this.symbolSet.full;
|
||||
} else if (this.symbolSet.half && halfRating <= rating && halfRating > rating - 0.5) {
|
||||
// Half symbol
|
||||
span.textContent = this.symbolSet.half;
|
||||
} else {
|
||||
// Empty symbol
|
||||
span.textContent = this.symbolSet.empty;
|
||||
}
|
||||
// Reset any styling for regular symbols
|
||||
span.style.opacity = '1';
|
||||
span.style.filter = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -226,42 +285,42 @@ class RatingWidget extends WidgetType {
|
|||
}
|
||||
|
||||
if (LOGGING_ENABLED) {
|
||||
console.debug('[InteractiveRatings] Render rating with half-symbol support', {
|
||||
console.debug('[InteractiveRatings] Render rating with full-only support', {
|
||||
rating,
|
||||
hasHalf: !!this.symbolSet.half,
|
||||
isFullOnly,
|
||||
symbolSet: this.symbolSet
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update rating in the document with half-symbol support
|
||||
* Update rating in the document with half-symbol support and full-only symbols
|
||||
*/
|
||||
private updateRating(view: EditorView, newRating: number): void {
|
||||
try {
|
||||
// Use Unicode character count for proper emoji handling
|
||||
const unicodeLength = getUnicodeCharLength(this.pattern);
|
||||
const isFullOnly = isFullOnlySymbol(this.symbolSet);
|
||||
|
||||
// Generate new symbol string with half-symbol support
|
||||
const newSymbols = generateSymbolsString(
|
||||
// For full-only symbols, use the disk-safe function that only includes rated symbols
|
||||
const newSymbols = generateSymbolsStringForDisk(
|
||||
newRating,
|
||||
unicodeLength,
|
||||
getUnicodeCharLength(this.pattern),
|
||||
this.symbolSet.full,
|
||||
this.symbolSet.empty,
|
||||
this.symbolSet.half || '',
|
||||
!!this.symbolSet.half
|
||||
!!this.symbolSet.half && !isFullOnly,
|
||||
this.symbolSet
|
||||
);
|
||||
|
||||
// Generate new rating text if it exists
|
||||
let newText = newSymbols;
|
||||
if (this.ratingText) {
|
||||
// Use the Unicode character count as the denominator for final update
|
||||
const newRatingText = formatRatingText(
|
||||
this.ratingText.format,
|
||||
newRating,
|
||||
unicodeLength,
|
||||
unicodeLength, // Use symbol count as denominator
|
||||
!!this.symbolSet.half
|
||||
this.ratingText.denominator, // Use original denominator
|
||||
this.ratingText.denominator,
|
||||
!!this.symbolSet.half && !isFullOnly
|
||||
);
|
||||
newText = newSymbols + newRatingText;
|
||||
}
|
||||
|
|
@ -276,17 +335,15 @@ class RatingWidget extends WidgetType {
|
|||
});
|
||||
|
||||
if (LOGGING_ENABLED) {
|
||||
console.info('[InteractiveRatings] Rating updated in editor with half-symbol support', {
|
||||
console.info('[InteractiveRatings] Rating updated in editor with full-only support', {
|
||||
oldRating: this.rating,
|
||||
newRating,
|
||||
newSymbols,
|
||||
newText,
|
||||
hasRatingText: !!this.ratingText,
|
||||
hasHalf: !!this.symbolSet.half,
|
||||
oldDenominator: this.ratingText?.denominator,
|
||||
newDenominator: unicodeLength,
|
||||
unicodeLengthCalculated: unicodeLength,
|
||||
rawPatternLength: this.pattern.length,
|
||||
isFullOnly,
|
||||
originalDenominator: this.ratingText?.denominator,
|
||||
position: { from: this.startPos, to: this.endPos }
|
||||
});
|
||||
}
|
||||
|
|
@ -343,9 +400,25 @@ const ratingViewPlugin = ViewPlugin.fromClass(
|
|||
if (!symbolSet) continue;
|
||||
|
||||
const rating = calculateRating(pattern, symbolSet);
|
||||
const isFullOnly = isFullOnlySymbol(symbolSet);
|
||||
|
||||
// For full-only symbols, skip if rating is 0 (not supported)
|
||||
if (isFullOnly && rating === 0) continue;
|
||||
|
||||
// Check for rating text after the symbols - pass UTF-16 positions
|
||||
const ratingText = parseRatingText(text, start, end);
|
||||
|
||||
// For full-only symbols, rating text is highly recommended
|
||||
if (isFullOnly && !ratingText) {
|
||||
if (LOGGING_ENABLED) {
|
||||
console.debug('[InteractiveRatings] Skipping full-only symbol without rating text', {
|
||||
pattern,
|
||||
symbolSet
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const actualEnd = ratingText ? ratingText.endPosition : end;
|
||||
|
||||
if (LOGGING_ENABLED) {
|
||||
|
|
@ -358,7 +431,8 @@ const ratingViewPlugin = ViewPlugin.fromClass(
|
|||
rating,
|
||||
hasRatingText: !!ratingText,
|
||||
ratingTextDetails: ratingText,
|
||||
symbolSet: symbolSet
|
||||
symbolSet: symbolSet,
|
||||
isFullOnly
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -420,11 +494,13 @@ const ratingViewPlugin = ViewPlugin.fromClass(
|
|||
const skippedCount = filteredMatches.filter(m =>
|
||||
cursorPos >= m.start - 1 && cursorPos <= m.end + 1
|
||||
).length;
|
||||
const fullOnlyCount = filteredMatches.filter(m => isFullOnlySymbol(m.symbolSet)).length;
|
||||
console.debug(`[InteractiveRatings] Built ${filteredMatches.length - skippedCount}/${filteredMatches.length} rating decorations (${skippedCount} skipped due to cursor proximity)`, {
|
||||
cursorPos,
|
||||
withRatingText: filteredMatches.filter(m => m.ratingText).length,
|
||||
symbolsOnly: filteredMatches.filter(m => !m.ratingText).length,
|
||||
withHalfSymbols: filteredMatches.filter(m => m.symbolSet.half).length
|
||||
withHalfSymbols: filteredMatches.filter(m => m.symbolSet.half).length,
|
||||
fullOnlySymbols: fullOnlyCount
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
46
src/utils.ts
46
src/utils.ts
|
|
@ -1,4 +1,5 @@
|
|||
import { LOGGING_ENABLED } from './constants';
|
||||
import { SymbolSet } from './types';
|
||||
|
||||
/**
|
||||
* Get the length of a string in Unicode characters
|
||||
|
|
@ -24,6 +25,13 @@ export function utf16ToUnicodePosition(str: string, utf16Position: number): numb
|
|||
return getUnicodeCharLength(utf16Substring);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a symbol set is full-only (same symbol for full and empty, no half)
|
||||
*/
|
||||
export function isFullOnlySymbol(symbolSet: SymbolSet): boolean {
|
||||
return symbolSet.full === symbolSet.empty && !symbolSet.half;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the new rating based on cursor position
|
||||
*/
|
||||
|
|
@ -67,7 +75,8 @@ export function calculateNewRating(overlay: HTMLElement, clientX: number): numbe
|
|||
}
|
||||
|
||||
/**
|
||||
* Generate a string of rating symbols
|
||||
* Generate a string of rating symbols for display purposes
|
||||
* For full-only symbols with rating text, this shows denominator count symbols
|
||||
*/
|
||||
export function generateSymbolsString(
|
||||
rating: number,
|
||||
|
|
@ -75,11 +84,17 @@ export function generateSymbolsString(
|
|||
full: string,
|
||||
empty: string,
|
||||
half: string,
|
||||
supportsHalf: boolean
|
||||
supportsHalf: boolean,
|
||||
symbolSet?: SymbolSet,
|
||||
denominator?: number
|
||||
): string {
|
||||
// For full-only symbols with rating text, use denominator for total count
|
||||
const isFullOnly = symbolSet && isFullOnlySymbol(symbolSet);
|
||||
const totalSymbols = (isFullOnly && denominator) ? denominator : symbolCount;
|
||||
|
||||
let newSymbols = '';
|
||||
|
||||
for (let i = 0; i < symbolCount; i++) {
|
||||
for (let i = 0; i < totalSymbols; i++) {
|
||||
if (i < Math.floor(rating)) {
|
||||
newSymbols += full;
|
||||
} else if (supportsHalf && i === Math.floor(rating) && rating % 1 !== 0) {
|
||||
|
|
@ -93,10 +108,13 @@ export function generateSymbolsString(
|
|||
console.debug(`[InteractiveRatings] Generated symbols string`, {
|
||||
rating,
|
||||
symbolCount,
|
||||
totalSymbols,
|
||||
full,
|
||||
empty,
|
||||
half,
|
||||
supportsHalf,
|
||||
isFullOnly,
|
||||
denominator,
|
||||
newSymbols
|
||||
});
|
||||
};
|
||||
|
|
@ -104,6 +122,28 @@ export function generateSymbolsString(
|
|||
return newSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a string of rating symbols for writing to disk (full-only symbols only include rated ones)
|
||||
*/
|
||||
export function generateSymbolsStringForDisk(
|
||||
rating: number,
|
||||
symbolCount: number,
|
||||
full: string,
|
||||
empty: string,
|
||||
half: string,
|
||||
supportsHalf: boolean,
|
||||
symbolSet?: SymbolSet
|
||||
): string {
|
||||
// For full-only symbols, only write the rated symbols
|
||||
if (symbolSet && isFullOnlySymbol(symbolSet)) {
|
||||
const ratedCount = Math.floor(rating);
|
||||
return full.repeat(ratedCount);
|
||||
}
|
||||
|
||||
// For regular symbols, use the standard logic
|
||||
return generateSymbolsString(rating, symbolCount, full, empty, half, supportsHalf, symbolSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the rating text based on the specified format
|
||||
*/
|
||||
|
|
|
|||
33
styles.css
33
styles.css
|
|
@ -43,6 +43,7 @@
|
|||
position: relative;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
transition: opacity 0.2s ease, filter 0.2s ease;
|
||||
}
|
||||
|
||||
/* Enhanced hover effect for symbols with half-symbol visual feedback */
|
||||
|
|
@ -74,6 +75,28 @@
|
|||
/* No visual elements */
|
||||
}
|
||||
|
||||
/* Full-only symbol styles - auto-detected symbols that use grayed-out effect */
|
||||
.interactive-rating-editor-widget[data-full-only="true"] .interactive-rating-symbols span {
|
||||
transition: opacity 0.2s ease, filter 0.2s ease;
|
||||
}
|
||||
|
||||
/* Ensure full-only unrated symbols are properly grayed out */
|
||||
.interactive-rating-editor-widget[data-full-only="true"] .interactive-rating-symbols span[style*="opacity: 0.5"] {
|
||||
opacity: 0.5 !important;
|
||||
filter: grayscale(100%) !important;
|
||||
}
|
||||
|
||||
/* Ensure full-only rated symbols are fully visible */
|
||||
.interactive-rating-editor-widget[data-full-only="true"] .interactive-rating-symbols span[style*="opacity: 1"] {
|
||||
opacity: 1 !important;
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
/* Support for display symbol count different from pattern length */
|
||||
.interactive-rating-editor-widget[data-display-symbol-count] .interactive-rating-symbols {
|
||||
/* Styles for symbols with extended display count */
|
||||
}
|
||||
|
||||
/* Mobile/touch enhancements for editor */
|
||||
@media (max-width: 768px) {
|
||||
.interactive-rating-editor-widget {
|
||||
|
|
@ -96,6 +119,12 @@
|
|||
.interactive-rating-editor-widget[data-supports-half="true"]:hover .interactive-rating-symbols span::before {
|
||||
/* No visual elements */
|
||||
}
|
||||
|
||||
/* Enhanced contrast for full-only symbols */
|
||||
.interactive-rating-editor-widget[data-full-only="true"] .interactive-rating-symbols span[style*="opacity: 0.5"] {
|
||||
opacity: 0.3 !important;
|
||||
filter: grayscale(100%) contrast(0.7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduced motion accessibility */
|
||||
|
|
@ -104,7 +133,7 @@
|
|||
.interactive-rating-editor-widget .interactive-rating-symbols span,
|
||||
.interactive-rating-editor-widget .interactive-rating-symbols span::before,
|
||||
.interactive-rating-editor-widget .interactive-rating-symbols span::after {
|
||||
/* No transitions to remove */
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,5 +148,5 @@
|
|||
|
||||
/* Smooth symbol transitions during preview */
|
||||
.interactive-rating-editor-widget .interactive-rating-symbols span {
|
||||
/* No transitions */
|
||||
/* Transitions defined above */
|
||||
}
|
||||
Loading…
Reference in a new issue