@seekora-ai/ui-sdk-vanilla 0.2.11 → 0.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * RatingDisplay - Vanilla JS
3
+ *
4
+ * Star rating display with review count and multiple variants
5
+ */
6
+ export type RatingVariant = 'stars-only' | 'compact' | 'full' | 'inline';
7
+ export type RatingSize = 'small' | 'medium' | 'large';
8
+ export interface RatingDisplayOptions {
9
+ rating: number;
10
+ reviewCount?: number;
11
+ variant?: RatingVariant;
12
+ size?: RatingSize;
13
+ maxRating?: number;
14
+ showNumeric?: boolean;
15
+ showHalfStars?: boolean;
16
+ interactive?: boolean;
17
+ onRatingChange?: (rating: number) => void;
18
+ starColor?: string;
19
+ emptyStarColor?: string;
20
+ textColor?: string;
21
+ showReviewCount?: boolean;
22
+ reviewCountFormat?: (count: number) => string;
23
+ className?: string;
24
+ }
25
+ export declare class RatingDisplay {
26
+ private container;
27
+ private options;
28
+ private hoverRating;
29
+ constructor(containerOrSelector: string | HTMLElement, options: RatingDisplayOptions);
30
+ private defaultReviewCountFormat;
31
+ private createStarSVG;
32
+ private renderStars;
33
+ private render;
34
+ private attachEventListeners;
35
+ update(options: Partial<RatingDisplayOptions>): void;
36
+ destroy(): void;
37
+ }
38
+ export declare function createRatingDisplay(container: string | HTMLElement, options: RatingDisplayOptions): RatingDisplay;
39
+ //# sourceMappingURL=rating-display.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rating-display.d.ts","sourceRoot":"","sources":["../../src/components/rating-display.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AACzE,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgCD,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,WAAW,CAAuB;gBAE9B,mBAAmB,EAAE,MAAM,GAAG,WAAW,EAAE,OAAO,EAAE,oBAAoB;IA6BpF,OAAO,CAAC,wBAAwB;IAMhC,OAAO,CAAC,aAAa;IAmCrB,OAAO,CAAC,WAAW;IA6BnB,OAAO,CAAC,MAAM;IAyDd,OAAO,CAAC,oBAAoB;IAwBrB,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,IAAI;IAKpD,OAAO,IAAI,IAAI;CAGvB;AAGD,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,WAAW,EAC/B,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAEf"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * RatingDisplay - Vanilla JS
3
+ *
4
+ * Star rating display with review count and multiple variants
5
+ */
6
+ const sizeMap = {
7
+ small: 14,
8
+ medium: 18,
9
+ large: 24,
10
+ };
11
+ const fontSizeMap = {
12
+ small: '0.75rem',
13
+ medium: '0.875rem',
14
+ large: '1rem',
15
+ };
16
+ export class RatingDisplay {
17
+ constructor(containerOrSelector, options) {
18
+ this.hoverRating = null;
19
+ this.container = typeof containerOrSelector === 'string'
20
+ ? document.querySelector(containerOrSelector)
21
+ : containerOrSelector;
22
+ if (!this.container) {
23
+ throw new Error('RatingDisplay: Container element not found');
24
+ }
25
+ this.options = {
26
+ variant: 'compact',
27
+ size: 'medium',
28
+ maxRating: 5,
29
+ showNumeric: false,
30
+ showHalfStars: true,
31
+ interactive: false,
32
+ starColor: '#f59e0b',
33
+ emptyStarColor: '#d1d5db',
34
+ textColor: 'var(--seekora-text-secondary, #6b7280)',
35
+ showReviewCount: true,
36
+ onRatingChange: () => { },
37
+ reviewCountFormat: this.defaultReviewCountFormat,
38
+ className: '',
39
+ ...options,
40
+ };
41
+ this.render();
42
+ }
43
+ defaultReviewCountFormat(count) {
44
+ if (count >= 1000000)
45
+ return `${(count / 1000000).toFixed(1)}M`;
46
+ if (count >= 1000)
47
+ return `${(count / 1000).toFixed(1)}K`;
48
+ return count.toString();
49
+ }
50
+ createStarSVG(filled, half, size) {
51
+ const { starColor, emptyStarColor } = this.options;
52
+ if (half) {
53
+ return `
54
+ <svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
55
+ <defs>
56
+ <linearGradient id="half-fill-${Math.random()}">
57
+ <stop offset="50%" stop-color="${starColor}" />
58
+ <stop offset="50%" stop-color="${emptyStarColor}" />
59
+ </linearGradient>
60
+ </defs>
61
+ <path
62
+ d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"
63
+ fill="url(#half-fill-${Math.random()})"
64
+ stroke="${starColor}"
65
+ stroke-width="1"
66
+ />
67
+ </svg>
68
+ `;
69
+ }
70
+ return `
71
+ <svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="${filled ? starColor : 'none'}" xmlns="http://www.w3.org/2000/svg">
72
+ <path
73
+ d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"
74
+ stroke="${filled ? starColor : emptyStarColor}"
75
+ stroke-width="1.5"
76
+ stroke-linecap="round"
77
+ stroke-linejoin="round"
78
+ />
79
+ </svg>
80
+ `;
81
+ }
82
+ renderStars() {
83
+ const { rating, maxRating, showHalfStars, interactive } = this.options;
84
+ const displayRating = interactive && this.hoverRating !== null ? this.hoverRating : rating;
85
+ const starSize = sizeMap[this.options.size];
86
+ let starsHTML = '';
87
+ for (let i = 1; i <= maxRating; i++) {
88
+ const filled = i <= Math.floor(displayRating);
89
+ const half = showHalfStars &&
90
+ i === Math.ceil(displayRating) &&
91
+ displayRating % 1 >= 0.25 &&
92
+ displayRating % 1 < 0.75;
93
+ const classes = `seekora-rating-star ${filled ? 'seekora-rating-star--filled' : 'seekora-rating-star--empty'} ${interactive ? 'seekora-rating-star--interactive' : ''}`;
94
+ starsHTML += `
95
+ <span
96
+ class="${classes}"
97
+ data-rating="${i}"
98
+ style="display: inline-block; width: ${starSize}px; height: ${starSize}px; cursor: ${interactive ? 'pointer' : 'default'};"
99
+ >
100
+ ${this.createStarSVG(filled, half, starSize)}
101
+ </span>
102
+ `;
103
+ }
104
+ return `<div style="display: inline-flex; align-items: center; gap: 2px;">${starsHTML}</div>`;
105
+ }
106
+ render() {
107
+ const { variant, rating, reviewCount, showNumeric, showReviewCount, textColor, maxRating, className } = this.options;
108
+ const fontSize = fontSizeMap[this.options.size];
109
+ const clampedRating = Math.max(0, Math.min(maxRating, rating));
110
+ let html = '';
111
+ if (variant === 'stars-only') {
112
+ html = `
113
+ <div class="seekora-rating-display seekora-rating-display--stars-only ${className}"
114
+ style="display: inline-flex; align-items: center; gap: 2px;">
115
+ ${this.renderStars()}
116
+ </div>
117
+ `;
118
+ }
119
+ else if (variant === 'compact') {
120
+ html = `
121
+ <div class="seekora-rating-display seekora-rating-display--compact ${className}"
122
+ style="display: inline-flex; align-items: center; gap: 4px; font-size: ${fontSize};">
123
+ ${this.renderStars()}
124
+ ${showNumeric ? `<span class="seekora-rating-numeric" style="font-weight: 600; color: ${textColor};">${clampedRating.toFixed(1)}</span>` : ''}
125
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `<span class="seekora-rating-review-count" style="color: ${textColor};">(${this.options.reviewCountFormat(reviewCount)})</span>` : ''}
126
+ </div>
127
+ `;
128
+ }
129
+ else if (variant === 'inline') {
130
+ html = `
131
+ <div class="seekora-rating-display seekora-rating-display--inline ${className}"
132
+ style="display: inline-flex; align-items: center; gap: 6px; font-size: ${fontSize};">
133
+ <span class="seekora-rating-numeric" style="font-weight: 600; color: var(--seekora-text-primary, #111827);">${clampedRating.toFixed(1)}</span>
134
+ ${this.renderStars()}
135
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `<span class="seekora-rating-review-count" style="color: ${textColor};">(${this.options.reviewCountFormat(reviewCount)})</span>` : ''}
136
+ </div>
137
+ `;
138
+ }
139
+ else if (variant === 'full') {
140
+ html = `
141
+ <div class="seekora-rating-display seekora-rating-display--full ${className}"
142
+ style="display: flex; flex-direction: column; gap: 4px; font-size: ${fontSize};">
143
+ <div style="display: flex; align-items: center; gap: 6px;">
144
+ ${this.renderStars()}
145
+ <span class="seekora-rating-numeric" style="font-weight: 600; color: var(--seekora-text-primary, #111827);">${clampedRating.toFixed(1)}</span>
146
+ <span class="seekora-rating-max" style="color: ${textColor};">/ ${maxRating}</span>
147
+ </div>
148
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `
149
+ <span class="seekora-rating-review-text" style="font-size: 0.875em; color: ${textColor};">
150
+ Based on ${this.options.reviewCountFormat(reviewCount)} ${reviewCount === 1 ? 'review' : 'reviews'}
151
+ </span>
152
+ ` : ''}
153
+ </div>
154
+ `;
155
+ }
156
+ this.container.innerHTML = html;
157
+ if (this.options.interactive) {
158
+ this.attachEventListeners();
159
+ }
160
+ }
161
+ attachEventListeners() {
162
+ const stars = this.container.querySelectorAll('.seekora-rating-star');
163
+ stars.forEach((star) => {
164
+ star.addEventListener('mouseenter', () => {
165
+ const rating = parseInt(star.dataset.rating || '0');
166
+ this.hoverRating = rating;
167
+ this.render();
168
+ });
169
+ star.addEventListener('click', () => {
170
+ const rating = parseInt(star.dataset.rating || '0');
171
+ this.hoverRating = null;
172
+ this.options.onRatingChange(rating);
173
+ this.render();
174
+ });
175
+ });
176
+ this.container.addEventListener('mouseleave', () => {
177
+ this.hoverRating = null;
178
+ this.render();
179
+ });
180
+ }
181
+ update(options) {
182
+ this.options = { ...this.options, ...options };
183
+ this.render();
184
+ }
185
+ destroy() {
186
+ this.container.innerHTML = '';
187
+ }
188
+ }
189
+ // Factory function for easier usage
190
+ export function createRatingDisplay(container, options) {
191
+ return new RatingDisplay(container, options);
192
+ }
package/dist/index.d.ts CHANGED
@@ -5,6 +5,45 @@ import * as _seekora_ai_ui_sdk_types from '@seekora-ai/ui-sdk-types';
5
5
  import { Theme, ThemeConfig, ViewMode, FieldMapping, ResultItem } from '@seekora-ai/ui-sdk-types';
6
6
  export { FieldMapping, ResultItem, SuggestionItem, Theme, ThemeConfig, ViewMode } from '@seekora-ai/ui-sdk-types';
7
7
 
8
+ /**
9
+ * RatingDisplay - Vanilla JS
10
+ *
11
+ * Star rating display with review count and multiple variants
12
+ */
13
+ type RatingVariant = 'stars-only' | 'compact' | 'full' | 'inline';
14
+ type RatingSize = 'small' | 'medium' | 'large';
15
+ interface RatingDisplayOptions {
16
+ rating: number;
17
+ reviewCount?: number;
18
+ variant?: RatingVariant;
19
+ size?: RatingSize;
20
+ maxRating?: number;
21
+ showNumeric?: boolean;
22
+ showHalfStars?: boolean;
23
+ interactive?: boolean;
24
+ onRatingChange?: (rating: number) => void;
25
+ starColor?: string;
26
+ emptyStarColor?: string;
27
+ textColor?: string;
28
+ showReviewCount?: boolean;
29
+ reviewCountFormat?: (count: number) => string;
30
+ className?: string;
31
+ }
32
+ declare class RatingDisplay {
33
+ private container;
34
+ private options;
35
+ private hoverRating;
36
+ constructor(containerOrSelector: string | HTMLElement, options: RatingDisplayOptions);
37
+ private defaultReviewCountFormat;
38
+ private createStarSVG;
39
+ private renderStars;
40
+ private render;
41
+ private attachEventListeners;
42
+ update(options: Partial<RatingDisplayOptions>): void;
43
+ destroy(): void;
44
+ }
45
+ declare function createRatingDisplay(container: string | HTMLElement, options: RatingDisplayOptions): RatingDisplay;
46
+
8
47
  /**
9
48
  * SearchProvider
10
49
  *
@@ -669,5 +708,5 @@ declare const minimalTheme: Theme;
669
708
 
670
709
  declare const createTheme: (config: ThemeConfig) => _seekora_ai_ui_sdk_types.Theme;
671
710
 
672
- export { ClearRefinements, CurrentRefinements, Facets, HitsPerPage, InfiniteHits, Pagination, QuerySuggestions, RangeInput, SearchBar, SearchLayout, SearchManager, SearchProvider, SearchResults, SortBy, Stats, createTheme, darkTheme, defaultTheme, minimalTheme };
673
- export type { ClearRefinementsOptions, CurrentRefinementsOptions, Facet, FacetItem, FacetsOptions, HitsPerPageItem, HitsPerPageOptions, InfiniteHitsOptions, PaginationOptions, QuerySuggestionsOptions, RangeInputOptions, Refinement, SearchBarOptions, SearchLayoutOptions, SearchManagerConfig, SearchProviderConfig, SearchResultsOptions, SearchState, SortByOptions, SortOption, StatsOptions };
711
+ export { ClearRefinements, CurrentRefinements, Facets, HitsPerPage, InfiniteHits, Pagination, QuerySuggestions, RangeInput, RatingDisplay, SearchBar, SearchLayout, SearchManager, SearchProvider, SearchResults, SortBy, Stats, createRatingDisplay, createTheme, darkTheme, defaultTheme, minimalTheme };
712
+ export type { ClearRefinementsOptions, CurrentRefinementsOptions, Facet, FacetItem, FacetsOptions, HitsPerPageItem, HitsPerPageOptions, InfiniteHitsOptions, PaginationOptions, QuerySuggestionsOptions, RangeInputOptions, RatingDisplayOptions, RatingSize, RatingVariant, Refinement, SearchBarOptions, SearchLayoutOptions, SearchManagerConfig, SearchProviderConfig, SearchResultsOptions, SearchState, SortByOptions, SortOption, StatsOptions };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,YAAY,EACV,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,aAAa,EACb,UAAU,GACX,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,aAAa,EACb,KAAK,EACL,SAAS,GACV,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,yBAAyB,EACzB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EACV,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,mBAAmB,EACnB,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAGhC,YAAY,EACV,KAAK,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,cAAc,GACf,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,YAAY,EACV,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,aAAa,EACb,UAAU,GACX,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACV,aAAa,EACb,KAAK,EACL,SAAS,GACV,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,yBAAyB,EACzB,UAAU,GACX,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EACV,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,kBAAkB,EAClB,eAAe,GAChB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACV,mBAAmB,EACnB,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAGhC,YAAY,EACV,KAAK,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,QAAQ,EACR,cAAc,GACf,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.esm.js CHANGED
@@ -1,6 +1,199 @@
1
1
  import { createTheme as createTheme$1, log, SearchStateManager, extractField, formatPrice } from '@seekora-ai/ui-sdk-core';
2
2
  export { getHighlightedValue, getSnippetedValue, highlightQuery, mergeThemes, parseHighlightedParts, parseQueryHighlightParts, stripHighlightTags } from '@seekora-ai/ui-sdk-core';
3
3
 
4
+ /**
5
+ * RatingDisplay - Vanilla JS
6
+ *
7
+ * Star rating display with review count and multiple variants
8
+ */
9
+ const sizeMap = {
10
+ small: 14,
11
+ medium: 18,
12
+ large: 24,
13
+ };
14
+ const fontSizeMap = {
15
+ small: '0.75rem',
16
+ medium: '0.875rem',
17
+ large: '1rem',
18
+ };
19
+ class RatingDisplay {
20
+ constructor(containerOrSelector, options) {
21
+ this.hoverRating = null;
22
+ this.container = typeof containerOrSelector === 'string'
23
+ ? document.querySelector(containerOrSelector)
24
+ : containerOrSelector;
25
+ if (!this.container) {
26
+ throw new Error('RatingDisplay: Container element not found');
27
+ }
28
+ this.options = {
29
+ variant: 'compact',
30
+ size: 'medium',
31
+ maxRating: 5,
32
+ showNumeric: false,
33
+ showHalfStars: true,
34
+ interactive: false,
35
+ starColor: '#f59e0b',
36
+ emptyStarColor: '#d1d5db',
37
+ textColor: 'var(--seekora-text-secondary, #6b7280)',
38
+ showReviewCount: true,
39
+ onRatingChange: () => { },
40
+ reviewCountFormat: this.defaultReviewCountFormat,
41
+ className: '',
42
+ ...options,
43
+ };
44
+ this.render();
45
+ }
46
+ defaultReviewCountFormat(count) {
47
+ if (count >= 1000000)
48
+ return `${(count / 1000000).toFixed(1)}M`;
49
+ if (count >= 1000)
50
+ return `${(count / 1000).toFixed(1)}K`;
51
+ return count.toString();
52
+ }
53
+ createStarSVG(filled, half, size) {
54
+ const { starColor, emptyStarColor } = this.options;
55
+ if (half) {
56
+ return `
57
+ <svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
58
+ <defs>
59
+ <linearGradient id="half-fill-${Math.random()}">
60
+ <stop offset="50%" stop-color="${starColor}" />
61
+ <stop offset="50%" stop-color="${emptyStarColor}" />
62
+ </linearGradient>
63
+ </defs>
64
+ <path
65
+ d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"
66
+ fill="url(#half-fill-${Math.random()})"
67
+ stroke="${starColor}"
68
+ stroke-width="1"
69
+ />
70
+ </svg>
71
+ `;
72
+ }
73
+ return `
74
+ <svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="${filled ? starColor : 'none'}" xmlns="http://www.w3.org/2000/svg">
75
+ <path
76
+ d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"
77
+ stroke="${filled ? starColor : emptyStarColor}"
78
+ stroke-width="1.5"
79
+ stroke-linecap="round"
80
+ stroke-linejoin="round"
81
+ />
82
+ </svg>
83
+ `;
84
+ }
85
+ renderStars() {
86
+ const { rating, maxRating, showHalfStars, interactive } = this.options;
87
+ const displayRating = interactive && this.hoverRating !== null ? this.hoverRating : rating;
88
+ const starSize = sizeMap[this.options.size];
89
+ let starsHTML = '';
90
+ for (let i = 1; i <= maxRating; i++) {
91
+ const filled = i <= Math.floor(displayRating);
92
+ const half = showHalfStars &&
93
+ i === Math.ceil(displayRating) &&
94
+ displayRating % 1 >= 0.25 &&
95
+ displayRating % 1 < 0.75;
96
+ const classes = `seekora-rating-star ${filled ? 'seekora-rating-star--filled' : 'seekora-rating-star--empty'} ${interactive ? 'seekora-rating-star--interactive' : ''}`;
97
+ starsHTML += `
98
+ <span
99
+ class="${classes}"
100
+ data-rating="${i}"
101
+ style="display: inline-block; width: ${starSize}px; height: ${starSize}px; cursor: ${interactive ? 'pointer' : 'default'};"
102
+ >
103
+ ${this.createStarSVG(filled, half, starSize)}
104
+ </span>
105
+ `;
106
+ }
107
+ return `<div style="display: inline-flex; align-items: center; gap: 2px;">${starsHTML}</div>`;
108
+ }
109
+ render() {
110
+ const { variant, rating, reviewCount, showNumeric, showReviewCount, textColor, maxRating, className } = this.options;
111
+ const fontSize = fontSizeMap[this.options.size];
112
+ const clampedRating = Math.max(0, Math.min(maxRating, rating));
113
+ let html = '';
114
+ if (variant === 'stars-only') {
115
+ html = `
116
+ <div class="seekora-rating-display seekora-rating-display--stars-only ${className}"
117
+ style="display: inline-flex; align-items: center; gap: 2px;">
118
+ ${this.renderStars()}
119
+ </div>
120
+ `;
121
+ }
122
+ else if (variant === 'compact') {
123
+ html = `
124
+ <div class="seekora-rating-display seekora-rating-display--compact ${className}"
125
+ style="display: inline-flex; align-items: center; gap: 4px; font-size: ${fontSize};">
126
+ ${this.renderStars()}
127
+ ${showNumeric ? `<span class="seekora-rating-numeric" style="font-weight: 600; color: ${textColor};">${clampedRating.toFixed(1)}</span>` : ''}
128
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `<span class="seekora-rating-review-count" style="color: ${textColor};">(${this.options.reviewCountFormat(reviewCount)})</span>` : ''}
129
+ </div>
130
+ `;
131
+ }
132
+ else if (variant === 'inline') {
133
+ html = `
134
+ <div class="seekora-rating-display seekora-rating-display--inline ${className}"
135
+ style="display: inline-flex; align-items: center; gap: 6px; font-size: ${fontSize};">
136
+ <span class="seekora-rating-numeric" style="font-weight: 600; color: var(--seekora-text-primary, #111827);">${clampedRating.toFixed(1)}</span>
137
+ ${this.renderStars()}
138
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `<span class="seekora-rating-review-count" style="color: ${textColor};">(${this.options.reviewCountFormat(reviewCount)})</span>` : ''}
139
+ </div>
140
+ `;
141
+ }
142
+ else if (variant === 'full') {
143
+ html = `
144
+ <div class="seekora-rating-display seekora-rating-display--full ${className}"
145
+ style="display: flex; flex-direction: column; gap: 4px; font-size: ${fontSize};">
146
+ <div style="display: flex; align-items: center; gap: 6px;">
147
+ ${this.renderStars()}
148
+ <span class="seekora-rating-numeric" style="font-weight: 600; color: var(--seekora-text-primary, #111827);">${clampedRating.toFixed(1)}</span>
149
+ <span class="seekora-rating-max" style="color: ${textColor};">/ ${maxRating}</span>
150
+ </div>
151
+ ${showReviewCount && reviewCount != null && reviewCount > 0 ? `
152
+ <span class="seekora-rating-review-text" style="font-size: 0.875em; color: ${textColor};">
153
+ Based on ${this.options.reviewCountFormat(reviewCount)} ${reviewCount === 1 ? 'review' : 'reviews'}
154
+ </span>
155
+ ` : ''}
156
+ </div>
157
+ `;
158
+ }
159
+ this.container.innerHTML = html;
160
+ if (this.options.interactive) {
161
+ this.attachEventListeners();
162
+ }
163
+ }
164
+ attachEventListeners() {
165
+ const stars = this.container.querySelectorAll('.seekora-rating-star');
166
+ stars.forEach((star) => {
167
+ star.addEventListener('mouseenter', () => {
168
+ const rating = parseInt(star.dataset.rating || '0');
169
+ this.hoverRating = rating;
170
+ this.render();
171
+ });
172
+ star.addEventListener('click', () => {
173
+ const rating = parseInt(star.dataset.rating || '0');
174
+ this.hoverRating = null;
175
+ this.options.onRatingChange(rating);
176
+ this.render();
177
+ });
178
+ });
179
+ this.container.addEventListener('mouseleave', () => {
180
+ this.hoverRating = null;
181
+ this.render();
182
+ });
183
+ }
184
+ update(options) {
185
+ this.options = { ...this.options, ...options };
186
+ this.render();
187
+ }
188
+ destroy() {
189
+ this.container.innerHTML = '';
190
+ }
191
+ }
192
+ // Factory function for easier usage
193
+ function createRatingDisplay(container, options) {
194
+ return new RatingDisplay(container, options);
195
+ }
196
+
4
197
  /**
5
198
  * Default Theme
6
199
  */
@@ -4032,5 +4225,5 @@ const minimalTheme = {
4032
4225
  },
4033
4226
  };
4034
4227
 
4035
- export { ClearRefinements, CurrentRefinements, Facets, HitsPerPage, InfiniteHits, Pagination, QuerySuggestions, RangeInput, SearchBar, SearchLayout, SearchManager, SearchProvider, SearchResults, SortBy, Stats, createTheme, darkTheme, defaultTheme, minimalTheme };
4228
+ export { ClearRefinements, CurrentRefinements, Facets, HitsPerPage, InfiniteHits, Pagination, QuerySuggestions, RangeInput, RatingDisplay, SearchBar, SearchLayout, SearchManager, SearchProvider, SearchResults, SortBy, Stats, createRatingDisplay, createTheme, darkTheme, defaultTheme, minimalTheme };
4036
4229
  //# sourceMappingURL=index.esm.js.map