@neovici/cosmoz-rating 1.0.0

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.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # cosmoz-rating
2
+
3
+ A pionjs rating web components
@@ -0,0 +1,9 @@
1
+ import { CosmozRatingElement } from '../../types/cosmoz-rating.types';
2
+ declare const useRating: (host: CosmozRatingElement) => {
3
+ rating: number | null;
4
+ disabled: boolean;
5
+ maxRating: number;
6
+ handleComponentLeave: () => void;
7
+ renderStar: (index: number) => import("lit-html").TemplateResult<1>;
8
+ };
9
+ export default useRating;
@@ -0,0 +1,95 @@
1
+ import { useState, html, useEffect } from '@pionjs/pion';
2
+ import { svg } from 'lit-html';
3
+ const useRating = (host) => {
4
+ const [hoveredRating, setHoveredRating] = useState(null);
5
+ const ratingAttr = host.getAttribute('rating');
6
+ const rating = ratingAttr ? parseFloat(ratingAttr) : null;
7
+ const disabled = host.hasAttribute('disabled');
8
+ const maxRatingAttr = host.getAttribute('max-rating');
9
+ const maxRating = maxRatingAttr ? parseInt(maxRatingAttr, 10) : 5;
10
+ useEffect(() => {
11
+ // Set appropriate tabIndex for accessibility
12
+ if (!disabled && host.tabIndex === -1) {
13
+ host.tabIndex = 0;
14
+ }
15
+ else if (disabled) {
16
+ host.tabIndex = -1;
17
+ }
18
+ }, [disabled]);
19
+ const getStarClass = (starIndex) => {
20
+ const currentRating = hoveredRating ?? rating;
21
+ if (currentRating === null || currentRating === undefined) {
22
+ return 'star';
23
+ }
24
+ if (starIndex <= Math.floor(currentRating)) {
25
+ return 'star filled';
26
+ }
27
+ else if (starIndex === Math.ceil(currentRating) &&
28
+ currentRating % 1 !== 0) {
29
+ return 'star partial';
30
+ }
31
+ return 'star';
32
+ };
33
+ const handleStarClick = (starRating) => {
34
+ if (disabled)
35
+ return;
36
+ host.dispatchEvent(new CustomEvent('change', {
37
+ detail: { rating: starRating },
38
+ bubbles: true,
39
+ composed: true,
40
+ }));
41
+ };
42
+ const handleStarHover = (starRating) => {
43
+ if (disabled)
44
+ return;
45
+ setHoveredRating(starRating);
46
+ };
47
+ const handleComponentLeave = () => {
48
+ if (disabled)
49
+ return;
50
+ setHoveredRating(null);
51
+ };
52
+ const renderStar = (index) => {
53
+ const starRating = index + 1;
54
+ const starClass = getStarClass(starRating);
55
+ const isPartial = starClass.includes('partial');
56
+ let fillPercentage = 0;
57
+ if (isPartial && rating !== null) {
58
+ fillPercentage = Math.round((rating % 1) * 100);
59
+ }
60
+ const starPath = 'M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z';
61
+ const partialPaths = svg `<defs>
62
+ <clipPath id="clip-${index}">
63
+ <rect x="0" y="0" width="${fillPercentage}%" height="100%" />
64
+ </clipPath>
65
+ </defs>
66
+ <!-- Background (empty) star -->
67
+ <path d="${starPath}" fill="var(--rating-star-color-empty)"></path>
68
+ <!-- Partial fill (clipped) star -->
69
+ <path
70
+ d="${starPath}"
71
+ fill="var(--rating-star-color)"
72
+ clip-path="url(#clip-${index})"
73
+ ></path>`;
74
+ return html `
75
+ <svg
76
+ class="${starClass}"
77
+ @click="${() => handleStarClick(starRating)}"
78
+ @mouseenter="${() => handleStarHover(starRating)}"
79
+ viewBox="0 0 24 24"
80
+ xmlns="http://www.w3.org/2000/svg"
81
+ >
82
+ ${isPartial
83
+ ? partialPaths
84
+ : svg `<path
85
+ d="${starPath}"
86
+ fill="${starClass.includes('filled')
87
+ ? 'var(--rating-star-color)'
88
+ : 'var(--rating-star-color-empty)'}"
89
+ ></path>`}
90
+ </svg>
91
+ `;
92
+ };
93
+ return { rating, disabled, maxRating, handleComponentLeave, renderStar };
94
+ };
95
+ export default useRating;
@@ -0,0 +1 @@
1
+ export * from './rating';
@@ -0,0 +1 @@
1
+ export * from './rating';
@@ -0,0 +1 @@
1
+ export declare const styles: string;
@@ -0,0 +1,36 @@
1
+ import { css } from '@pionjs/pion';
2
+ export const styles = css `
3
+ :host {
4
+ display: inline-block;
5
+ --rating-star-color: #ffd700;
6
+ --rating-star-color-empty: #d3d3d3;
7
+ --rating-star-color-hover: #ffed4a;
8
+ --rating-star-size: 24px;
9
+ --rating-star-gap: 2px;
10
+ }
11
+
12
+ :host([disabled]) {
13
+ pointer-events: none;
14
+ }
15
+
16
+ .rating-container {
17
+ display: flex;
18
+ gap: var(--rating-star-gap);
19
+ align-items: center;
20
+ }
21
+
22
+ .star {
23
+ width: var(--rating-star-size);
24
+ height: var(--rating-star-size);
25
+ cursor: pointer;
26
+ transition: fill 0.2s ease;
27
+ }
28
+
29
+ :host([disabled]) .star {
30
+ cursor: default;
31
+ }
32
+
33
+ .star:hover path {
34
+ color: var(--rating-star-color-hover) !important;
35
+ }
36
+ `;
@@ -0,0 +1,4 @@
1
+ import { CosmozRatingElement } from '../types/cosmoz-rating.types';
2
+ declare const CosmozRating: import("@pionjs/pion/lib/component").ComponentConstructor<CosmozRatingElement>;
3
+ export { CosmozRating };
4
+ export default CosmozRating;
@@ -0,0 +1,19 @@
1
+ import { component, html } from '@pionjs/pion';
2
+ import useRating from './hooks/use-rating';
3
+ import { styles } from './rating.css';
4
+ const Rating = (host) => {
5
+ const { maxRating, renderStar, handleComponentLeave } = useRating(host);
6
+ return html `
7
+ <div class="rating-container" @mouseleave=${handleComponentLeave}>
8
+ ${Array.from({ length: maxRating }, (_, index) => renderStar(index))}
9
+ </div>
10
+ `;
11
+ };
12
+ const CosmozRating = component(Rating, {
13
+ observedAttributes: ['rating', 'disabled', 'max-rating'],
14
+ useShadowDOM: true,
15
+ styleSheets: [styles],
16
+ });
17
+ customElements.define('cosmoz-rating', CosmozRating);
18
+ export { CosmozRating };
19
+ export default CosmozRating;
@@ -0,0 +1,12 @@
1
+ export interface RatingEvent extends CustomEvent {
2
+ detail: {
3
+ rating: number;
4
+ };
5
+ }
6
+ export interface CosmozRatingProps {
7
+ rating?: number | null;
8
+ disabled?: boolean;
9
+ maxRating?: number;
10
+ }
11
+ export interface CosmozRatingElement extends HTMLElement, CosmozRatingProps {
12
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@neovici/cosmoz-rating",
3
+ "version": "1.0.0",
4
+ "description": "A simple rating web component",
5
+ "keywords": [
6
+ "web-components",
7
+ "rating",
8
+ "stars",
9
+ "pion",
10
+ "lit-html"
11
+ ],
12
+ "homepage": "https://github.com/neovici/cosmoz-rating#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/neovici/cosmoz-rating/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/neovici/cosmoz-rating.git"
19
+ },
20
+ "license": "Apache-2.0",
21
+ "author": "",
22
+ "main": "dist/index.js",
23
+ "directories": {
24
+ "test": "test"
25
+ },
26
+ "scripts": {
27
+ "lint": "tsc && eslint --cache .",
28
+ "build": "tsc -p tsconfig.build.json",
29
+ "start": "wds",
30
+ "test": "wtr --coverage",
31
+ "test:watch": "wtr --watch",
32
+ "dev": "npm run storybook:start",
33
+ "storybook:start": "storybook dev -p 8000",
34
+ "storybook:build": "storybook build",
35
+ "storybook:deploy": "storybook-to-ghpages",
36
+ "storybook:preview": "npm run storybook:build && http-server -d ./storybook-static/",
37
+ "prepare": "husky"
38
+ },
39
+ "release": {
40
+ "plugins": [
41
+ "@semantic-release/commit-analyzer",
42
+ "@semantic-release/release-notes-generator",
43
+ "@semantic-release/changelog",
44
+ "@semantic-release/github",
45
+ "@semantic-release/npm",
46
+ "@semantic-release/git"
47
+ ],
48
+ "branch": "master",
49
+ "preset": "conventionalcommits"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "files": [
55
+ "dist/",
56
+ "types"
57
+ ],
58
+ "exports": {
59
+ ".": "./dist/index.js"
60
+ },
61
+ "dependencies": {
62
+ "@pionjs/pion": "^2.5.2",
63
+ "lit-html": "^3.3.1"
64
+ },
65
+ "devDependencies": {
66
+ "@commitlint/cli": "^19.2.1",
67
+ "@commitlint/config-conventional": "^19.1.0",
68
+ "@eslint/eslintrc": "^2.0.0",
69
+ "@neovici/cfg": "^2.0.0",
70
+ "@open-wc/testing": "^4.0.0",
71
+ "@semantic-release/changelog": "^6.0.0",
72
+ "@semantic-release/git": "^10.0.0",
73
+ "@storybook/addon-essentials": "^7.0.0",
74
+ "@storybook/addon-links": "^7.0.0",
75
+ "@storybook/web-components": "7.6.17",
76
+ "@types/mocha": "^10.0.6",
77
+ "@types/node": "^22.10.2",
78
+ "@web/storybook-builder": "^0.1.9",
79
+ "@web/storybook-framework-web-components": "^0.1.1",
80
+ "esbuild": "^0.24.0",
81
+ "http-server": "^14.1.1",
82
+ "husky": "^9.0.11",
83
+ "rollup-plugin-esbuild": "^6.1.1",
84
+ "semantic-release": "^24.0.0",
85
+ "sinon": "^19.0.0",
86
+ "storybook": "^7.0.0",
87
+ "typescript": "^5.4.3"
88
+ },
89
+ "overrides": {
90
+ "conventional-changelog-conventionalcommits": ">= 8.0.0"
91
+ }
92
+ }
@@ -0,0 +1,13 @@
1
+ export interface RatingEvent extends CustomEvent {
2
+ detail: {
3
+ rating: number;
4
+ };
5
+ }
6
+
7
+ export interface CosmozRatingProps {
8
+ rating?: number | null;
9
+ disabled?: boolean;
10
+ maxRating?: number;
11
+ }
12
+
13
+ export interface CosmozRatingElement extends HTMLElement, CosmozRatingProps {}