@ssejal8/recipe-ui-kit 0.1.0 → 0.1.1
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 +108 -277
- package/dist/cjs/index-BEr8BsSA.js +1712 -0
- package/dist/cjs/index-BEr8BsSA.js.map +1 -0
- package/dist/cjs/index.cjs.js +5 -0
- package/dist/cjs/index.cjs.js.map +1 -0
- package/dist/cjs/loader.cjs.js +15 -0
- package/dist/cjs/loader.cjs.js.map +1 -0
- package/dist/cjs/recipe-kit.cjs.js +27 -0
- package/dist/cjs/recipe-kit.cjs.js.map +1 -0
- package/dist/cjs/rk-badge_10.cjs.entry.js +638 -0
- package/dist/cjs/rk-badge_10.cjs.entry.js.map +1 -0
- package/dist/collection/collection-manifest.json +22 -0
- package/dist/collection/components/rk-badge/rk-badge.js +97 -0
- package/dist/collection/components/rk-badge/rk-badge.js.map +1 -0
- package/dist/collection/components/rk-chip/rk-chip.js +245 -0
- package/dist/collection/components/rk-chip/rk-chip.js.map +1 -0
- package/dist/collection/components/rk-empty-state/rk-empty-state.js +138 -0
- package/dist/collection/components/rk-empty-state/rk-empty-state.js.map +1 -0
- package/dist/collection/components/rk-meal-slot/rk-meal-slot.js +312 -0
- package/dist/collection/components/rk-meal-slot/rk-meal-slot.js.map +1 -0
- package/dist/collection/components/rk-modal/rk-modal.js +256 -0
- package/dist/collection/components/rk-modal/rk-modal.js.map +1 -0
- package/dist/collection/components/rk-rating/rk-rating.js +228 -0
- package/dist/collection/components/rk-rating/rk-rating.js.map +1 -0
- package/dist/collection/components/rk-recipe-card/rk-recipe-card.js +366 -0
- package/dist/collection/components/rk-recipe-card/rk-recipe-card.js.map +1 -0
- package/dist/collection/components/rk-search-bar/rk-search-bar.js +291 -0
- package/dist/collection/components/rk-search-bar/rk-search-bar.js.map +1 -0
- package/dist/collection/components/rk-segmented-control/rk-segmented-control.js +196 -0
- package/dist/collection/components/rk-segmented-control/rk-segmented-control.js.map +1 -0
- package/dist/collection/components/rk-tag-input/rk-tag-input.js +264 -0
- package/dist/collection/components/rk-tag-input/rk-tag-input.js.map +1 -0
- package/dist/collection/index.js +2 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/rk-badge.d.ts +11 -0
- package/dist/components/rk-badge.js +2 -0
- package/dist/components/rk-badge.js.map +1 -0
- package/dist/components/rk-chip.d.ts +11 -0
- package/dist/components/rk-chip.js +2 -0
- package/dist/components/rk-chip.js.map +1 -0
- package/dist/components/rk-empty-state.d.ts +11 -0
- package/dist/components/rk-empty-state.js +2 -0
- package/dist/components/rk-empty-state.js.map +1 -0
- package/dist/components/rk-meal-slot.d.ts +11 -0
- package/dist/components/rk-meal-slot.js +2 -0
- package/dist/components/rk-meal-slot.js.map +1 -0
- package/dist/components/rk-modal.d.ts +11 -0
- package/dist/components/rk-modal.js +2 -0
- package/dist/components/rk-modal.js.map +1 -0
- package/dist/components/rk-rating.d.ts +11 -0
- package/dist/components/rk-rating.js +2 -0
- package/dist/components/rk-rating.js.map +1 -0
- package/dist/components/rk-recipe-card.d.ts +11 -0
- package/dist/components/rk-recipe-card.js +2 -0
- package/dist/components/rk-recipe-card.js.map +1 -0
- package/dist/components/rk-search-bar.d.ts +11 -0
- package/dist/components/rk-search-bar.js +2 -0
- package/dist/components/rk-search-bar.js.map +1 -0
- package/dist/components/rk-segmented-control.d.ts +11 -0
- package/dist/components/rk-segmented-control.js +2 -0
- package/dist/components/rk-segmented-control.js.map +1 -0
- package/dist/components/rk-tag-input.d.ts +11 -0
- package/dist/components/rk-tag-input.js +2 -0
- package/dist/components/rk-tag-input.js.map +1 -0
- package/dist/esm/index-Bxo0C8lB.js +1684 -0
- package/dist/esm/index-Bxo0C8lB.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/loader.js +13 -0
- package/dist/esm/loader.js.map +1 -0
- package/dist/esm/recipe-kit.js +23 -0
- package/dist/esm/recipe-kit.js.map +1 -0
- package/dist/esm/rk-badge_10.entry.js +627 -0
- package/dist/esm/rk-badge_10.entry.js.map +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/recipe-kit/index.esm.js +2 -0
- package/dist/recipe-kit/index.esm.js.map +1 -0
- package/dist/recipe-kit/p-Bxo0C8lB.js +3 -0
- package/dist/recipe-kit/p-Bxo0C8lB.js.map +1 -0
- package/dist/recipe-kit/p-fcb955c7.entry.js +2 -0
- package/dist/recipe-kit/p-fcb955c7.entry.js.map +1 -0
- package/dist/recipe-kit/recipe-kit.css +1 -0
- package/dist/recipe-kit/recipe-kit.esm.js +2 -0
- package/dist/recipe-kit/recipe-kit.esm.js.map +1 -0
- package/dist/recipe-kit/rk-global.css +104 -0
- package/dist/types/components/rk-badge/rk-badge.d.ts +15 -0
- package/dist/types/components/rk-chip/rk-chip.d.ts +37 -0
- package/dist/types/components/rk-empty-state/rk-empty-state.d.ts +19 -0
- package/dist/types/components/rk-meal-slot/rk-meal-slot.d.ts +53 -0
- package/dist/types/components/rk-modal/rk-modal.d.ts +41 -0
- package/dist/types/components/rk-rating/rk-rating.d.ts +26 -0
- package/dist/types/components/rk-recipe-card/rk-recipe-card.d.ts +67 -0
- package/dist/types/components/rk-search-bar/rk-search-bar.d.ts +39 -0
- package/dist/types/components/rk-segmented-control/rk-segmented-control.d.ts +37 -0
- package/dist/types/components/rk-tag-input/rk-tag-input.d.ts +35 -0
- package/{packages/recipe-kit-stencil/src → dist/types}/components.d.ts +1 -1
- package/{packages/recipe-kit-stencil/src/index.ts → dist/types/index.d.ts} +0 -2
- package/dist/types/stencil-public-runtime.d.ts +1873 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +84 -22
- package/.claude/settings.local.json +0 -10
- package/.npmr +0 -1
- package/apps/recipe-web/package.json +0 -31
- package/apps/recipe-web/src/app.css +0 -418
- package/apps/recipe-web/src/app.d.ts +0 -32
- package/apps/recipe-web/src/app.html +0 -25
- package/apps/recipe-web/src/lib/api/mealdb.ts +0 -240
- package/apps/recipe-web/src/lib/components/AppHeader.svelte +0 -290
- package/apps/recipe-web/src/lib/components/ConfirmDialog.svelte +0 -46
- package/apps/recipe-web/src/lib/components/Modal.svelte +0 -33
- package/apps/recipe-web/src/lib/components/RecipeCard.svelte +0 -100
- package/apps/recipe-web/src/lib/components/RecipeForm.svelte +0 -560
- package/apps/recipe-web/src/lib/components/RecipeGrid.svelte +0 -77
- package/apps/recipe-web/src/lib/components/RecipePickerDialog.svelte +0 -185
- package/apps/recipe-web/src/lib/components/Toaster.svelte +0 -111
- package/apps/recipe-web/src/lib/domain/recipe.ts +0 -130
- package/apps/recipe-web/src/lib/domain/validation.ts +0 -189
- package/apps/recipe-web/src/lib/stencil/WebComponent.svelte +0 -145
- package/apps/recipe-web/src/lib/stencil/register.ts +0 -28
- package/apps/recipe-web/src/lib/stores/favorites.svelte.ts +0 -81
- package/apps/recipe-web/src/lib/stores/myRecipes.svelte.ts +0 -98
- package/apps/recipe-web/src/lib/stores/persisted.svelte.ts +0 -73
- package/apps/recipe-web/src/lib/stores/planner.svelte.ts +0 -275
- package/apps/recipe-web/src/lib/stores/theme.svelte.ts +0 -67
- package/apps/recipe-web/src/lib/stores/toast.svelte.ts +0 -66
- package/apps/recipe-web/src/routes/+error.svelte +0 -36
- package/apps/recipe-web/src/routes/+layout.svelte +0 -62
- package/apps/recipe-web/src/routes/+page.svelte +0 -352
- package/apps/recipe-web/src/routes/+page.ts +0 -107
- package/apps/recipe-web/src/routes/favorites/+page.svelte +0 -170
- package/apps/recipe-web/src/routes/my-recipes/+layout.ts +0 -55
- package/apps/recipe-web/src/routes/my-recipes/+page.svelte +0 -180
- package/apps/recipe-web/src/routes/my-recipes/[id]/edit/+page.svelte +0 -104
- package/apps/recipe-web/src/routes/my-recipes/new/+page.svelte +0 -67
- package/apps/recipe-web/src/routes/planner/+page.svelte +0 -422
- package/apps/recipe-web/src/routes/recipes/[id]/+page.svelte +0 -538
- package/apps/recipe-web/src/routes/recipes/[id]/+page.ts +0 -24
- package/apps/recipe-web/static/favicon.svg +0 -8
- package/apps/recipe-web/static/robots.txt +0 -2
- package/apps/recipe-web/svelte.config.js +0 -35
- package/apps/recipe-web/tsconfig.json +0 -14
- package/apps/recipe-web/vite.config.ts +0 -23
- package/packages/recipe-kit-stencil/README.md +0 -185
- package/packages/recipe-kit-stencil/package-lock.json +0 -4772
- package/packages/recipe-kit-stencil/package.json +0 -91
- package/packages/recipe-kit-stencil/src/components/rk-badge/readme.md +0 -38
- package/packages/recipe-kit-stencil/src/components/rk-badge/rk-badge.tsx +0 -31
- package/packages/recipe-kit-stencil/src/components/rk-chip/readme.md +0 -52
- package/packages/recipe-kit-stencil/src/components/rk-chip/rk-chip.tsx +0 -105
- package/packages/recipe-kit-stencil/src/components/rk-empty-state/readme.md +0 -44
- package/packages/recipe-kit-stencil/src/components/rk-empty-state/rk-empty-state.tsx +0 -50
- package/packages/recipe-kit-stencil/src/components/rk-meal-slot/readme.md +0 -58
- package/packages/recipe-kit-stencil/src/components/rk-meal-slot/rk-meal-slot.tsx +0 -182
- package/packages/recipe-kit-stencil/src/components/rk-modal/readme.md +0 -57
- package/packages/recipe-kit-stencil/src/components/rk-modal/rk-modal.tsx +0 -176
- package/packages/recipe-kit-stencil/src/components/rk-rating/readme.md +0 -42
- package/packages/recipe-kit-stencil/src/components/rk-rating/rk-rating.tsx +0 -114
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/readme.md +0 -66
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/rk-recipe-card.tsx +0 -179
- package/packages/recipe-kit-stencil/src/components/rk-recipe-card/test/rk-recipe-card.spec.tsx +0 -70
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/readme.md +0 -67
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/rk-search-bar.tsx +0 -152
- package/packages/recipe-kit-stencil/src/components/rk-search-bar/test/rk-search-bar.spec.tsx +0 -93
- package/packages/recipe-kit-stencil/src/components/rk-segmented-control/readme.md +0 -41
- package/packages/recipe-kit-stencil/src/components/rk-segmented-control/rk-segmented-control.tsx +0 -115
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/readme.md +0 -47
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/rk-tag-input.tsx +0 -181
- package/packages/recipe-kit-stencil/src/components/rk-tag-input/test/rk-tag-input.spec.tsx +0 -76
- package/packages/recipe-kit-stencil/src/index.html +0 -259
- package/packages/recipe-kit-stencil/stencil.config.ts +0 -41
- package/packages/recipe-kit-stencil/tsconfig.json +0 -24
- /package/{packages/recipe-kit-stencil/LICENSE → LICENSE} +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-badge/rk-badge.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-chip/rk-chip.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-empty-state/rk-empty-state.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-meal-slot/rk-meal-slot.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-modal/rk-modal.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-rating/rk-rating.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-recipe-card/rk-recipe-card.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-search-bar/rk-search-bar.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-segmented-control/rk-segmented-control.css +0 -0
- /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-tag-input/rk-tag-input.css +0 -0
- /package/{packages/recipe-kit-stencil/src/global → dist/collection}/rk-global.css +0 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { h, Host } from "@stencil/core";
|
|
2
|
+
/**
|
|
3
|
+
* An accessible dialog with a backdrop, Escape handling and focus restoration.
|
|
4
|
+
*
|
|
5
|
+
* The component is presentational: it emits `rkModalClose` and lets the host
|
|
6
|
+
* application own the `open` state.
|
|
7
|
+
*
|
|
8
|
+
* @slot - Dialog body.
|
|
9
|
+
* @slot header - Replaces the default heading row entirely.
|
|
10
|
+
* @slot footer - Action row pinned to the bottom of the dialog.
|
|
11
|
+
*/
|
|
12
|
+
export class RkModal {
|
|
13
|
+
constructor() {
|
|
14
|
+
/** Controls visibility. */
|
|
15
|
+
this.open = false;
|
|
16
|
+
/** Heading text for the default header. */
|
|
17
|
+
this.heading = '';
|
|
18
|
+
/** Dialog width preset. */
|
|
19
|
+
this.size = 'md';
|
|
20
|
+
/** Set to false to require an explicit action to dismiss. */
|
|
21
|
+
this.dismissible = true;
|
|
22
|
+
this.hasFooter = false;
|
|
23
|
+
/** Collapses the footer region when no action row was projected into it. */
|
|
24
|
+
this.syncFooterSlot = () => {
|
|
25
|
+
this.hasFooter = Array.from(this.host.children).some(node => node.getAttribute('slot') === 'footer');
|
|
26
|
+
};
|
|
27
|
+
this.handleGlobalKeyDown = (event) => {
|
|
28
|
+
if (!this.open)
|
|
29
|
+
return;
|
|
30
|
+
if (event.key === 'Escape' && this.dismissible) {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
this.rkModalClose.emit();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (event.key === 'Tab') {
|
|
36
|
+
const focusable = this.focusableElements();
|
|
37
|
+
if (focusable.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const first = focusable[0];
|
|
40
|
+
const last = focusable[focusable.length - 1];
|
|
41
|
+
const active = (this.host.shadowRoot?.activeElement ?? document.activeElement);
|
|
42
|
+
if (event.shiftKey && active === first) {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
last.focus();
|
|
45
|
+
}
|
|
46
|
+
else if (!event.shiftKey && active === last) {
|
|
47
|
+
event.preventDefault();
|
|
48
|
+
first.focus();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
this.handleBackdrop = (event) => {
|
|
53
|
+
if (!this.dismissible)
|
|
54
|
+
return;
|
|
55
|
+
if (event.target === event.currentTarget)
|
|
56
|
+
this.rkModalClose.emit();
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
onOpenChange(isOpen, wasOpen) {
|
|
60
|
+
if (isOpen === wasOpen)
|
|
61
|
+
return;
|
|
62
|
+
if (isOpen) {
|
|
63
|
+
this.previouslyFocused = document.activeElement;
|
|
64
|
+
document.addEventListener('keydown', this.handleGlobalKeyDown);
|
|
65
|
+
this.lockScroll(true);
|
|
66
|
+
// Wait a frame so the panel exists before moving focus into it.
|
|
67
|
+
requestAnimationFrame(() => this.focusFirst());
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
document.removeEventListener('keydown', this.handleGlobalKeyDown);
|
|
71
|
+
this.lockScroll(false);
|
|
72
|
+
this.previouslyFocused?.focus?.();
|
|
73
|
+
this.previouslyFocused = undefined;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
componentWillLoad() {
|
|
77
|
+
this.syncFooterSlot();
|
|
78
|
+
}
|
|
79
|
+
componentDidLoad() {
|
|
80
|
+
if (this.open)
|
|
81
|
+
this.onOpenChange(true, false);
|
|
82
|
+
}
|
|
83
|
+
disconnectedCallback() {
|
|
84
|
+
document.removeEventListener('keydown', this.handleGlobalKeyDown);
|
|
85
|
+
this.lockScroll(false);
|
|
86
|
+
}
|
|
87
|
+
lockScroll(locked) {
|
|
88
|
+
if (typeof document === 'undefined')
|
|
89
|
+
return;
|
|
90
|
+
document.documentElement.style.overflow = locked ? 'hidden' : '';
|
|
91
|
+
}
|
|
92
|
+
focusableElements() {
|
|
93
|
+
if (!this.panelEl)
|
|
94
|
+
return [];
|
|
95
|
+
const selector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
96
|
+
// Slotted content lives in the light DOM, so both trees have to be queried.
|
|
97
|
+
const inShadow = Array.from(this.panelEl.querySelectorAll(selector));
|
|
98
|
+
const inLight = Array.from(this.host.querySelectorAll(selector));
|
|
99
|
+
return [...inShadow, ...inLight].filter(el => el.offsetParent !== null || el === document.activeElement);
|
|
100
|
+
}
|
|
101
|
+
focusFirst() {
|
|
102
|
+
const [first] = this.focusableElements();
|
|
103
|
+
(first ?? this.panelEl)?.focus?.();
|
|
104
|
+
}
|
|
105
|
+
render() {
|
|
106
|
+
if (!this.open)
|
|
107
|
+
return h(Host, { "aria-hidden": "true" });
|
|
108
|
+
return (h(Host, null, h("div", { class: "backdrop", part: "backdrop", onClick: this.handleBackdrop }, h("div", { class: { panel: true, [`size-${this.size}`]: true }, part: "panel", role: "dialog", "aria-modal": "true", "aria-label": this.heading || 'Dialog', tabindex: -1, ref: el => (this.panelEl = el) }, h("header", { class: "header", part: "header" }, h("slot", { name: "header" }, h("div", { class: "titles" }, h("h2", { class: "heading", part: "heading" }, this.heading), this.subheading && h("p", { class: "subheading" }, this.subheading))), this.dismissible && (h("button", { class: "close", part: "close", type: "button", "aria-label": "Close dialog", onClick: () => this.rkModalClose.emit() }, h("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "aria-hidden": "true" }, h("path", { d: "M6 6l12 12M18 6 6 18" }))))), h("div", { class: "body", part: "body" }, h("slot", null)), h("footer", { class: { footer: true, 'is-empty': !this.hasFooter }, part: "footer" }, h("slot", { name: "footer", onSlotchange: this.syncFooterSlot }))))));
|
|
109
|
+
}
|
|
110
|
+
static get is() { return "rk-modal"; }
|
|
111
|
+
static get encapsulation() { return "shadow"; }
|
|
112
|
+
static get originalStyleUrls() {
|
|
113
|
+
return {
|
|
114
|
+
"$": ["rk-modal.css"]
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
static get styleUrls() {
|
|
118
|
+
return {
|
|
119
|
+
"$": ["rk-modal.css"]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
static get properties() {
|
|
123
|
+
return {
|
|
124
|
+
"open": {
|
|
125
|
+
"type": "boolean",
|
|
126
|
+
"mutable": false,
|
|
127
|
+
"complexType": {
|
|
128
|
+
"original": "boolean",
|
|
129
|
+
"resolved": "boolean",
|
|
130
|
+
"references": {}
|
|
131
|
+
},
|
|
132
|
+
"required": false,
|
|
133
|
+
"optional": false,
|
|
134
|
+
"docs": {
|
|
135
|
+
"tags": [],
|
|
136
|
+
"text": "Controls visibility."
|
|
137
|
+
},
|
|
138
|
+
"getter": false,
|
|
139
|
+
"setter": false,
|
|
140
|
+
"reflect": false,
|
|
141
|
+
"attribute": "open",
|
|
142
|
+
"defaultValue": "false"
|
|
143
|
+
},
|
|
144
|
+
"heading": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"mutable": false,
|
|
147
|
+
"complexType": {
|
|
148
|
+
"original": "string",
|
|
149
|
+
"resolved": "string",
|
|
150
|
+
"references": {}
|
|
151
|
+
},
|
|
152
|
+
"required": false,
|
|
153
|
+
"optional": false,
|
|
154
|
+
"docs": {
|
|
155
|
+
"tags": [],
|
|
156
|
+
"text": "Heading text for the default header."
|
|
157
|
+
},
|
|
158
|
+
"getter": false,
|
|
159
|
+
"setter": false,
|
|
160
|
+
"reflect": false,
|
|
161
|
+
"attribute": "heading",
|
|
162
|
+
"defaultValue": "''"
|
|
163
|
+
},
|
|
164
|
+
"subheading": {
|
|
165
|
+
"type": "string",
|
|
166
|
+
"mutable": false,
|
|
167
|
+
"complexType": {
|
|
168
|
+
"original": "string",
|
|
169
|
+
"resolved": "string",
|
|
170
|
+
"references": {}
|
|
171
|
+
},
|
|
172
|
+
"required": false,
|
|
173
|
+
"optional": true,
|
|
174
|
+
"docs": {
|
|
175
|
+
"tags": [],
|
|
176
|
+
"text": "Optional supporting line under the heading."
|
|
177
|
+
},
|
|
178
|
+
"getter": false,
|
|
179
|
+
"setter": false,
|
|
180
|
+
"reflect": false,
|
|
181
|
+
"attribute": "subheading"
|
|
182
|
+
},
|
|
183
|
+
"size": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"mutable": false,
|
|
186
|
+
"complexType": {
|
|
187
|
+
"original": "'sm' | 'md' | 'lg'",
|
|
188
|
+
"resolved": "\"lg\" | \"md\" | \"sm\"",
|
|
189
|
+
"references": {}
|
|
190
|
+
},
|
|
191
|
+
"required": false,
|
|
192
|
+
"optional": false,
|
|
193
|
+
"docs": {
|
|
194
|
+
"tags": [],
|
|
195
|
+
"text": "Dialog width preset."
|
|
196
|
+
},
|
|
197
|
+
"getter": false,
|
|
198
|
+
"setter": false,
|
|
199
|
+
"reflect": false,
|
|
200
|
+
"attribute": "size",
|
|
201
|
+
"defaultValue": "'md'"
|
|
202
|
+
},
|
|
203
|
+
"dismissible": {
|
|
204
|
+
"type": "boolean",
|
|
205
|
+
"mutable": false,
|
|
206
|
+
"complexType": {
|
|
207
|
+
"original": "boolean",
|
|
208
|
+
"resolved": "boolean",
|
|
209
|
+
"references": {}
|
|
210
|
+
},
|
|
211
|
+
"required": false,
|
|
212
|
+
"optional": false,
|
|
213
|
+
"docs": {
|
|
214
|
+
"tags": [],
|
|
215
|
+
"text": "Set to false to require an explicit action to dismiss."
|
|
216
|
+
},
|
|
217
|
+
"getter": false,
|
|
218
|
+
"setter": false,
|
|
219
|
+
"reflect": false,
|
|
220
|
+
"attribute": "dismissible",
|
|
221
|
+
"defaultValue": "true"
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
static get states() {
|
|
226
|
+
return {
|
|
227
|
+
"hasFooter": {}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
static get events() {
|
|
231
|
+
return [{
|
|
232
|
+
"method": "rkModalClose",
|
|
233
|
+
"name": "rkModalClose",
|
|
234
|
+
"bubbles": true,
|
|
235
|
+
"cancelable": true,
|
|
236
|
+
"composed": true,
|
|
237
|
+
"docs": {
|
|
238
|
+
"tags": [],
|
|
239
|
+
"text": "Emitted when the user requests dismissal (X, backdrop or Escape)."
|
|
240
|
+
},
|
|
241
|
+
"complexType": {
|
|
242
|
+
"original": "void",
|
|
243
|
+
"resolved": "void",
|
|
244
|
+
"references": {}
|
|
245
|
+
}
|
|
246
|
+
}];
|
|
247
|
+
}
|
|
248
|
+
static get elementRef() { return "host"; }
|
|
249
|
+
static get watchers() {
|
|
250
|
+
return [{
|
|
251
|
+
"propName": "open",
|
|
252
|
+
"methodName": "onOpenChange"
|
|
253
|
+
}];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=rk-modal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rk-modal.js","sourceRoot":"","sources":["../../../src/components/rk-modal/rk-modal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, Element, Event, EventEmitter, h, Host, Prop, State, Watch } from '@stencil/core';\n\n/**\n * An accessible dialog with a backdrop, Escape handling and focus restoration.\n *\n * The component is presentational: it emits `rkModalClose` and lets the host\n * application own the `open` state.\n *\n * @slot - Dialog body.\n * @slot header - Replaces the default heading row entirely.\n * @slot footer - Action row pinned to the bottom of the dialog.\n */\n@Component({\n tag: 'rk-modal',\n styleUrl: 'rk-modal.css',\n shadow: true,\n})\nexport class RkModal {\n @Element() host!: HTMLElement;\n\n /** Controls visibility. */\n @Prop() open = false;\n\n /** Heading text for the default header. */\n @Prop() heading = '';\n\n /** Optional supporting line under the heading. */\n @Prop() subheading?: string;\n\n /** Dialog width preset. */\n @Prop() size: 'sm' | 'md' | 'lg' = 'md';\n\n /** Set to false to require an explicit action to dismiss. */\n @Prop() dismissible = true;\n\n /** Emitted when the user requests dismissal (X, backdrop or Escape). */\n @Event({ eventName: 'rkModalClose' }) rkModalClose!: EventEmitter<void>;\n\n @State() private hasFooter = false;\n\n private previouslyFocused?: HTMLElement;\n private panelEl?: HTMLElement;\n\n /** Collapses the footer region when no action row was projected into it. */\n private syncFooterSlot = () => {\n this.hasFooter = Array.from(this.host.children).some(node => node.getAttribute('slot') === 'footer');\n };\n\n @Watch('open')\n protected onOpenChange(isOpen: boolean, wasOpen: boolean) {\n if (isOpen === wasOpen) return;\n\n if (isOpen) {\n this.previouslyFocused = document.activeElement as HTMLElement;\n document.addEventListener('keydown', this.handleGlobalKeyDown);\n this.lockScroll(true);\n // Wait a frame so the panel exists before moving focus into it.\n requestAnimationFrame(() => this.focusFirst());\n } else {\n document.removeEventListener('keydown', this.handleGlobalKeyDown);\n this.lockScroll(false);\n this.previouslyFocused?.focus?.();\n this.previouslyFocused = undefined;\n }\n }\n\n componentWillLoad() {\n this.syncFooterSlot();\n }\n\n componentDidLoad() {\n if (this.open) this.onOpenChange(true, false);\n }\n\n disconnectedCallback() {\n document.removeEventListener('keydown', this.handleGlobalKeyDown);\n this.lockScroll(false);\n }\n\n private lockScroll(locked: boolean) {\n if (typeof document === 'undefined') return;\n document.documentElement.style.overflow = locked ? 'hidden' : '';\n }\n\n private focusableElements(): HTMLElement[] {\n if (!this.panelEl) return [];\n const selector = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n // Slotted content lives in the light DOM, so both trees have to be queried.\n const inShadow = Array.from(this.panelEl.querySelectorAll<HTMLElement>(selector));\n const inLight = Array.from(this.host.querySelectorAll<HTMLElement>(selector));\n return [...inShadow, ...inLight].filter(el => el.offsetParent !== null || el === document.activeElement);\n }\n\n private focusFirst() {\n const [first] = this.focusableElements();\n (first ?? this.panelEl)?.focus?.();\n }\n\n private handleGlobalKeyDown = (event: KeyboardEvent) => {\n if (!this.open) return;\n\n if (event.key === 'Escape' && this.dismissible) {\n event.preventDefault();\n this.rkModalClose.emit();\n return;\n }\n\n if (event.key === 'Tab') {\n const focusable = this.focusableElements();\n if (focusable.length === 0) return;\n const first = focusable[0];\n const last = focusable[focusable.length - 1];\n const active = (this.host.shadowRoot?.activeElement ?? document.activeElement) as HTMLElement;\n\n if (event.shiftKey && active === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && active === last) {\n event.preventDefault();\n first.focus();\n }\n }\n };\n\n private handleBackdrop = (event: MouseEvent) => {\n if (!this.dismissible) return;\n if (event.target === event.currentTarget) this.rkModalClose.emit();\n };\n\n render() {\n if (!this.open) return <Host aria-hidden=\"true\" />;\n\n return (\n <Host>\n <div class=\"backdrop\" part=\"backdrop\" onClick={this.handleBackdrop}>\n <div\n class={{ panel: true, [`size-${this.size}`]: true }}\n part=\"panel\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={this.heading || 'Dialog'}\n tabindex={-1}\n ref={el => (this.panelEl = el)}\n >\n <header class=\"header\" part=\"header\">\n <slot name=\"header\">\n <div class=\"titles\">\n <h2 class=\"heading\" part=\"heading\">\n {this.heading}\n </h2>\n {this.subheading && <p class=\"subheading\">{this.subheading}</p>}\n </div>\n </slot>\n\n {this.dismissible && (\n <button class=\"close\" part=\"close\" type=\"button\" aria-label=\"Close dialog\" onClick={() => this.rkModalClose.emit()}>\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" aria-hidden=\"true\">\n <path d=\"M6 6l12 12M18 6 6 18\" />\n </svg>\n </button>\n )}\n </header>\n\n <div class=\"body\" part=\"body\">\n <slot />\n </div>\n\n <footer class={{ footer: true, 'is-empty': !this.hasFooter }} part=\"footer\">\n <slot name=\"footer\" onSlotchange={this.syncFooterSlot} />\n </footer>\n </div>\n </div>\n </Host>\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { h, Host } from "@stencil/core";
|
|
2
|
+
/**
|
|
3
|
+
* A star rating that works as a read-only display or an interactive input.
|
|
4
|
+
*/
|
|
5
|
+
export class RkRating {
|
|
6
|
+
constructor() {
|
|
7
|
+
/** Current rating. Supports halves when `readonly`. */
|
|
8
|
+
this.value = 0;
|
|
9
|
+
/** Number of stars. */
|
|
10
|
+
this.max = 5;
|
|
11
|
+
/** When true the stars are display-only and not focusable. */
|
|
12
|
+
this.readonly = false;
|
|
13
|
+
/** Star size in pixels. */
|
|
14
|
+
this.size = 18;
|
|
15
|
+
/** Accessible label for the group. */
|
|
16
|
+
this.label = 'Rating';
|
|
17
|
+
/** Renders the numeric value next to the stars. */
|
|
18
|
+
this.showValue = false;
|
|
19
|
+
this.hovered = 0;
|
|
20
|
+
this.handleKeyDown = (event) => {
|
|
21
|
+
if (this.readonly)
|
|
22
|
+
return;
|
|
23
|
+
const step = event.key === 'ArrowRight' || event.key === 'ArrowUp' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowDown' ? -1 : 0;
|
|
24
|
+
if (step === 0)
|
|
25
|
+
return;
|
|
26
|
+
event.preventDefault();
|
|
27
|
+
const next = Math.min(this.max, Math.max(0, Math.round(this.value) + step));
|
|
28
|
+
this.value = next;
|
|
29
|
+
this.rkRatingChange.emit(next);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
select(next) {
|
|
33
|
+
if (this.readonly)
|
|
34
|
+
return;
|
|
35
|
+
// Clicking the active star clears the rating, which is the usual affordance.
|
|
36
|
+
const resolved = next === this.value ? 0 : next;
|
|
37
|
+
this.value = resolved;
|
|
38
|
+
this.rkRatingChange.emit(resolved);
|
|
39
|
+
}
|
|
40
|
+
render() {
|
|
41
|
+
const active = this.hovered || this.value;
|
|
42
|
+
const stars = Array.from({ length: Math.max(1, Math.round(this.max)) }, (_, index) => index + 1);
|
|
43
|
+
return (h(Host, { key: '2147d57f9887cec317ac42156632a11a92d031bd', style: { '--rk-star-size': `${this.size}px` } }, h("div", { key: '8b6ed7883e64adbebb6cb00166ef1dc3e63d8b6f', class: { stars: true, 'is-readonly': this.readonly }, part: "stars", role: this.readonly ? 'img' : 'slider', "aria-label": `${this.label}: ${this.value} out of ${this.max}`, "aria-valuenow": this.readonly ? null : String(this.value), "aria-valuemin": this.readonly ? null : '0', "aria-valuemax": this.readonly ? null : String(this.max), tabindex: this.readonly ? null : 0, onKeyDown: this.handleKeyDown, onMouseLeave: () => (this.hovered = 0) }, stars.map(star => {
|
|
44
|
+
// Fractional fill only matters in read-only display mode.
|
|
45
|
+
const fill = this.readonly ? Math.min(1, Math.max(0, this.value - (star - 1))) : star <= active ? 1 : 0;
|
|
46
|
+
return (h("span", { class: "star-wrap", key: star, onMouseEnter: () => !this.readonly && (this.hovered = star), onClick: () => this.select(star), "aria-hidden": "true" }, h("svg", { class: "star", viewBox: "0 0 24 24" }, h("path", { class: "star-bg", d: "M12 2.6l2.86 5.98 6.54.87-4.8 4.5 1.2 6.45L12 17.3l-5.8 3.1 1.2-6.45-4.8-4.5 6.54-.87z" }), h("clipPath", { id: `clip-${star}` }, h("rect", { x: "0", y: "0", width: 24 * fill, height: "24" })), h("path", { class: "star-fg", "clip-path": `url(#clip-${star})`, d: "M12 2.6l2.86 5.98 6.54.87-4.8 4.5 1.2 6.45L12 17.3l-5.8 3.1 1.2-6.45-4.8-4.5 6.54-.87z" }))));
|
|
47
|
+
}), this.showValue && (h("span", { key: '0f3fd486aca3d9c05df656e723eeb6a30a7469c2', class: "value", part: "value" }, this.value ? this.value.toFixed(1) : '—', typeof this.count === 'number' && h("span", { key: '40a193f04a145644556dad621fcc3ee65fb101b3', class: "count" }, "(", this.count, ")"))))));
|
|
48
|
+
}
|
|
49
|
+
static get is() { return "rk-rating"; }
|
|
50
|
+
static get encapsulation() { return "shadow"; }
|
|
51
|
+
static get originalStyleUrls() {
|
|
52
|
+
return {
|
|
53
|
+
"$": ["rk-rating.css"]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
static get styleUrls() {
|
|
57
|
+
return {
|
|
58
|
+
"$": ["rk-rating.css"]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
static get properties() {
|
|
62
|
+
return {
|
|
63
|
+
"value": {
|
|
64
|
+
"type": "number",
|
|
65
|
+
"mutable": true,
|
|
66
|
+
"complexType": {
|
|
67
|
+
"original": "number",
|
|
68
|
+
"resolved": "number",
|
|
69
|
+
"references": {}
|
|
70
|
+
},
|
|
71
|
+
"required": false,
|
|
72
|
+
"optional": false,
|
|
73
|
+
"docs": {
|
|
74
|
+
"tags": [],
|
|
75
|
+
"text": "Current rating. Supports halves when `readonly`."
|
|
76
|
+
},
|
|
77
|
+
"getter": false,
|
|
78
|
+
"setter": false,
|
|
79
|
+
"reflect": false,
|
|
80
|
+
"attribute": "value",
|
|
81
|
+
"defaultValue": "0"
|
|
82
|
+
},
|
|
83
|
+
"max": {
|
|
84
|
+
"type": "number",
|
|
85
|
+
"mutable": false,
|
|
86
|
+
"complexType": {
|
|
87
|
+
"original": "number",
|
|
88
|
+
"resolved": "number",
|
|
89
|
+
"references": {}
|
|
90
|
+
},
|
|
91
|
+
"required": false,
|
|
92
|
+
"optional": false,
|
|
93
|
+
"docs": {
|
|
94
|
+
"tags": [],
|
|
95
|
+
"text": "Number of stars."
|
|
96
|
+
},
|
|
97
|
+
"getter": false,
|
|
98
|
+
"setter": false,
|
|
99
|
+
"reflect": false,
|
|
100
|
+
"attribute": "max",
|
|
101
|
+
"defaultValue": "5"
|
|
102
|
+
},
|
|
103
|
+
"readonly": {
|
|
104
|
+
"type": "boolean",
|
|
105
|
+
"mutable": false,
|
|
106
|
+
"complexType": {
|
|
107
|
+
"original": "boolean",
|
|
108
|
+
"resolved": "boolean",
|
|
109
|
+
"references": {}
|
|
110
|
+
},
|
|
111
|
+
"required": false,
|
|
112
|
+
"optional": false,
|
|
113
|
+
"docs": {
|
|
114
|
+
"tags": [],
|
|
115
|
+
"text": "When true the stars are display-only and not focusable."
|
|
116
|
+
},
|
|
117
|
+
"getter": false,
|
|
118
|
+
"setter": false,
|
|
119
|
+
"reflect": false,
|
|
120
|
+
"attribute": "readonly",
|
|
121
|
+
"defaultValue": "false"
|
|
122
|
+
},
|
|
123
|
+
"size": {
|
|
124
|
+
"type": "number",
|
|
125
|
+
"mutable": false,
|
|
126
|
+
"complexType": {
|
|
127
|
+
"original": "number",
|
|
128
|
+
"resolved": "number",
|
|
129
|
+
"references": {}
|
|
130
|
+
},
|
|
131
|
+
"required": false,
|
|
132
|
+
"optional": false,
|
|
133
|
+
"docs": {
|
|
134
|
+
"tags": [],
|
|
135
|
+
"text": "Star size in pixels."
|
|
136
|
+
},
|
|
137
|
+
"getter": false,
|
|
138
|
+
"setter": false,
|
|
139
|
+
"reflect": false,
|
|
140
|
+
"attribute": "size",
|
|
141
|
+
"defaultValue": "18"
|
|
142
|
+
},
|
|
143
|
+
"label": {
|
|
144
|
+
"type": "string",
|
|
145
|
+
"mutable": false,
|
|
146
|
+
"complexType": {
|
|
147
|
+
"original": "string",
|
|
148
|
+
"resolved": "string",
|
|
149
|
+
"references": {}
|
|
150
|
+
},
|
|
151
|
+
"required": false,
|
|
152
|
+
"optional": false,
|
|
153
|
+
"docs": {
|
|
154
|
+
"tags": [],
|
|
155
|
+
"text": "Accessible label for the group."
|
|
156
|
+
},
|
|
157
|
+
"getter": false,
|
|
158
|
+
"setter": false,
|
|
159
|
+
"reflect": false,
|
|
160
|
+
"attribute": "label",
|
|
161
|
+
"defaultValue": "'Rating'"
|
|
162
|
+
},
|
|
163
|
+
"showValue": {
|
|
164
|
+
"type": "boolean",
|
|
165
|
+
"mutable": false,
|
|
166
|
+
"complexType": {
|
|
167
|
+
"original": "boolean",
|
|
168
|
+
"resolved": "boolean",
|
|
169
|
+
"references": {}
|
|
170
|
+
},
|
|
171
|
+
"required": false,
|
|
172
|
+
"optional": false,
|
|
173
|
+
"docs": {
|
|
174
|
+
"tags": [],
|
|
175
|
+
"text": "Renders the numeric value next to the stars."
|
|
176
|
+
},
|
|
177
|
+
"getter": false,
|
|
178
|
+
"setter": false,
|
|
179
|
+
"reflect": false,
|
|
180
|
+
"attribute": "show-value",
|
|
181
|
+
"defaultValue": "false"
|
|
182
|
+
},
|
|
183
|
+
"count": {
|
|
184
|
+
"type": "number",
|
|
185
|
+
"mutable": false,
|
|
186
|
+
"complexType": {
|
|
187
|
+
"original": "number",
|
|
188
|
+
"resolved": "number",
|
|
189
|
+
"references": {}
|
|
190
|
+
},
|
|
191
|
+
"required": false,
|
|
192
|
+
"optional": true,
|
|
193
|
+
"docs": {
|
|
194
|
+
"tags": [],
|
|
195
|
+
"text": "Optional count of ratings, rendered as \"(24)\"."
|
|
196
|
+
},
|
|
197
|
+
"getter": false,
|
|
198
|
+
"setter": false,
|
|
199
|
+
"reflect": false,
|
|
200
|
+
"attribute": "count"
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
static get states() {
|
|
205
|
+
return {
|
|
206
|
+
"hovered": {}
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
static get events() {
|
|
210
|
+
return [{
|
|
211
|
+
"method": "rkRatingChange",
|
|
212
|
+
"name": "rkRatingChange",
|
|
213
|
+
"bubbles": true,
|
|
214
|
+
"cancelable": true,
|
|
215
|
+
"composed": true,
|
|
216
|
+
"docs": {
|
|
217
|
+
"tags": [],
|
|
218
|
+
"text": "Emitted when the user picks a new rating."
|
|
219
|
+
},
|
|
220
|
+
"complexType": {
|
|
221
|
+
"original": "number",
|
|
222
|
+
"resolved": "number",
|
|
223
|
+
"references": {}
|
|
224
|
+
}
|
|
225
|
+
}];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=rk-rating.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rk-rating.js","sourceRoot":"","sources":["../../../src/components/rk-rating/rk-rating.tsx"],"names":[],"mappings":"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","sourcesContent":["import { Component, Event, EventEmitter, h, Host, Prop, State } from '@stencil/core';\n\n/**\n * A star rating that works as a read-only display or an interactive input.\n */\n@Component({\n tag: 'rk-rating',\n styleUrl: 'rk-rating.css',\n shadow: true,\n})\nexport class RkRating {\n /** Current rating. Supports halves when `readonly`. */\n @Prop({ mutable: true }) value = 0;\n\n /** Number of stars. */\n @Prop() max = 5;\n\n /** When true the stars are display-only and not focusable. */\n @Prop() readonly = false;\n\n /** Star size in pixels. */\n @Prop() size = 18;\n\n /** Accessible label for the group. */\n @Prop() label = 'Rating';\n\n /** Renders the numeric value next to the stars. */\n @Prop() showValue = false;\n\n /** Optional count of ratings, rendered as \"(24)\". */\n @Prop() count?: number;\n\n /** Emitted when the user picks a new rating. */\n @Event({ eventName: 'rkRatingChange' }) rkRatingChange!: EventEmitter<number>;\n\n @State() private hovered = 0;\n\n private select(next: number) {\n if (this.readonly) return;\n // Clicking the active star clears the rating, which is the usual affordance.\n const resolved = next === this.value ? 0 : next;\n this.value = resolved;\n this.rkRatingChange.emit(resolved);\n }\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (this.readonly) return;\n const step = event.key === 'ArrowRight' || event.key === 'ArrowUp' ? 1 : event.key === 'ArrowLeft' || event.key === 'ArrowDown' ? -1 : 0;\n if (step === 0) return;\n event.preventDefault();\n const next = Math.min(this.max, Math.max(0, Math.round(this.value) + step));\n this.value = next;\n this.rkRatingChange.emit(next);\n };\n\n render() {\n const active = this.hovered || this.value;\n const stars = Array.from({ length: Math.max(1, Math.round(this.max)) }, (_, index) => index + 1);\n\n return (\n <Host style={{ '--rk-star-size': `${this.size}px` }}>\n <div\n class={{ stars: true, 'is-readonly': this.readonly }}\n part=\"stars\"\n role={this.readonly ? 'img' : 'slider'}\n aria-label={`${this.label}: ${this.value} out of ${this.max}`}\n aria-valuenow={this.readonly ? null : String(this.value)}\n aria-valuemin={this.readonly ? null : '0'}\n aria-valuemax={this.readonly ? null : String(this.max)}\n tabindex={this.readonly ? null : 0}\n onKeyDown={this.handleKeyDown}\n onMouseLeave={() => (this.hovered = 0)}\n >\n {stars.map(star => {\n // Fractional fill only matters in read-only display mode.\n const fill = this.readonly ? Math.min(1, Math.max(0, this.value - (star - 1))) : star <= active ? 1 : 0;\n\n return (\n <span\n class=\"star-wrap\"\n key={star}\n onMouseEnter={() => !this.readonly && (this.hovered = star)}\n onClick={() => this.select(star)}\n aria-hidden=\"true\"\n >\n <svg class=\"star\" viewBox=\"0 0 24 24\">\n <path\n class=\"star-bg\"\n d=\"M12 2.6l2.86 5.98 6.54.87-4.8 4.5 1.2 6.45L12 17.3l-5.8 3.1 1.2-6.45-4.8-4.5 6.54-.87z\"\n />\n <clipPath id={`clip-${star}`}>\n <rect x=\"0\" y=\"0\" width={24 * fill} height=\"24\" />\n </clipPath>\n <path\n class=\"star-fg\"\n clip-path={`url(#clip-${star})`}\n d=\"M12 2.6l2.86 5.98 6.54.87-4.8 4.5 1.2 6.45L12 17.3l-5.8 3.1 1.2-6.45-4.8-4.5 6.54-.87z\"\n />\n </svg>\n </span>\n );\n })}\n\n {this.showValue && (\n <span class=\"value\" part=\"value\">\n {this.value ? this.value.toFixed(1) : '—'}\n {typeof this.count === 'number' && <span class=\"count\">({this.count})</span>}\n </span>\n )}\n </div>\n </Host>\n );\n }\n}\n"]}
|