@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.
Files changed (191) hide show
  1. package/README.md +108 -277
  2. package/dist/cjs/index-BEr8BsSA.js +1712 -0
  3. package/dist/cjs/index-BEr8BsSA.js.map +1 -0
  4. package/dist/cjs/index.cjs.js +5 -0
  5. package/dist/cjs/index.cjs.js.map +1 -0
  6. package/dist/cjs/loader.cjs.js +15 -0
  7. package/dist/cjs/loader.cjs.js.map +1 -0
  8. package/dist/cjs/recipe-kit.cjs.js +27 -0
  9. package/dist/cjs/recipe-kit.cjs.js.map +1 -0
  10. package/dist/cjs/rk-badge_10.cjs.entry.js +638 -0
  11. package/dist/cjs/rk-badge_10.cjs.entry.js.map +1 -0
  12. package/dist/collection/collection-manifest.json +22 -0
  13. package/dist/collection/components/rk-badge/rk-badge.js +97 -0
  14. package/dist/collection/components/rk-badge/rk-badge.js.map +1 -0
  15. package/dist/collection/components/rk-chip/rk-chip.js +245 -0
  16. package/dist/collection/components/rk-chip/rk-chip.js.map +1 -0
  17. package/dist/collection/components/rk-empty-state/rk-empty-state.js +138 -0
  18. package/dist/collection/components/rk-empty-state/rk-empty-state.js.map +1 -0
  19. package/dist/collection/components/rk-meal-slot/rk-meal-slot.js +312 -0
  20. package/dist/collection/components/rk-meal-slot/rk-meal-slot.js.map +1 -0
  21. package/dist/collection/components/rk-modal/rk-modal.js +256 -0
  22. package/dist/collection/components/rk-modal/rk-modal.js.map +1 -0
  23. package/dist/collection/components/rk-rating/rk-rating.js +228 -0
  24. package/dist/collection/components/rk-rating/rk-rating.js.map +1 -0
  25. package/dist/collection/components/rk-recipe-card/rk-recipe-card.js +366 -0
  26. package/dist/collection/components/rk-recipe-card/rk-recipe-card.js.map +1 -0
  27. package/dist/collection/components/rk-search-bar/rk-search-bar.js +291 -0
  28. package/dist/collection/components/rk-search-bar/rk-search-bar.js.map +1 -0
  29. package/dist/collection/components/rk-segmented-control/rk-segmented-control.js +196 -0
  30. package/dist/collection/components/rk-segmented-control/rk-segmented-control.js.map +1 -0
  31. package/dist/collection/components/rk-tag-input/rk-tag-input.js +264 -0
  32. package/dist/collection/components/rk-tag-input/rk-tag-input.js.map +1 -0
  33. package/dist/collection/index.js +2 -0
  34. package/dist/collection/index.js.map +1 -0
  35. package/dist/components/index.d.ts +35 -0
  36. package/dist/components/index.js +2 -0
  37. package/dist/components/index.js.map +1 -0
  38. package/dist/components/rk-badge.d.ts +11 -0
  39. package/dist/components/rk-badge.js +2 -0
  40. package/dist/components/rk-badge.js.map +1 -0
  41. package/dist/components/rk-chip.d.ts +11 -0
  42. package/dist/components/rk-chip.js +2 -0
  43. package/dist/components/rk-chip.js.map +1 -0
  44. package/dist/components/rk-empty-state.d.ts +11 -0
  45. package/dist/components/rk-empty-state.js +2 -0
  46. package/dist/components/rk-empty-state.js.map +1 -0
  47. package/dist/components/rk-meal-slot.d.ts +11 -0
  48. package/dist/components/rk-meal-slot.js +2 -0
  49. package/dist/components/rk-meal-slot.js.map +1 -0
  50. package/dist/components/rk-modal.d.ts +11 -0
  51. package/dist/components/rk-modal.js +2 -0
  52. package/dist/components/rk-modal.js.map +1 -0
  53. package/dist/components/rk-rating.d.ts +11 -0
  54. package/dist/components/rk-rating.js +2 -0
  55. package/dist/components/rk-rating.js.map +1 -0
  56. package/dist/components/rk-recipe-card.d.ts +11 -0
  57. package/dist/components/rk-recipe-card.js +2 -0
  58. package/dist/components/rk-recipe-card.js.map +1 -0
  59. package/dist/components/rk-search-bar.d.ts +11 -0
  60. package/dist/components/rk-search-bar.js +2 -0
  61. package/dist/components/rk-search-bar.js.map +1 -0
  62. package/dist/components/rk-segmented-control.d.ts +11 -0
  63. package/dist/components/rk-segmented-control.js +2 -0
  64. package/dist/components/rk-segmented-control.js.map +1 -0
  65. package/dist/components/rk-tag-input.d.ts +11 -0
  66. package/dist/components/rk-tag-input.js +2 -0
  67. package/dist/components/rk-tag-input.js.map +1 -0
  68. package/dist/esm/index-Bxo0C8lB.js +1684 -0
  69. package/dist/esm/index-Bxo0C8lB.js.map +1 -0
  70. package/dist/esm/index.js +4 -0
  71. package/dist/esm/index.js.map +1 -0
  72. package/dist/esm/loader.js +13 -0
  73. package/dist/esm/loader.js.map +1 -0
  74. package/dist/esm/recipe-kit.js +23 -0
  75. package/dist/esm/recipe-kit.js.map +1 -0
  76. package/dist/esm/rk-badge_10.entry.js +627 -0
  77. package/dist/esm/rk-badge_10.entry.js.map +1 -0
  78. package/dist/index.cjs.js +1 -0
  79. package/dist/index.js +1 -0
  80. package/dist/recipe-kit/index.esm.js +2 -0
  81. package/dist/recipe-kit/index.esm.js.map +1 -0
  82. package/dist/recipe-kit/p-Bxo0C8lB.js +3 -0
  83. package/dist/recipe-kit/p-Bxo0C8lB.js.map +1 -0
  84. package/dist/recipe-kit/p-fcb955c7.entry.js +2 -0
  85. package/dist/recipe-kit/p-fcb955c7.entry.js.map +1 -0
  86. package/dist/recipe-kit/recipe-kit.css +1 -0
  87. package/dist/recipe-kit/recipe-kit.esm.js +2 -0
  88. package/dist/recipe-kit/recipe-kit.esm.js.map +1 -0
  89. package/dist/recipe-kit/rk-global.css +104 -0
  90. package/dist/types/components/rk-badge/rk-badge.d.ts +15 -0
  91. package/dist/types/components/rk-chip/rk-chip.d.ts +37 -0
  92. package/dist/types/components/rk-empty-state/rk-empty-state.d.ts +19 -0
  93. package/dist/types/components/rk-meal-slot/rk-meal-slot.d.ts +53 -0
  94. package/dist/types/components/rk-modal/rk-modal.d.ts +41 -0
  95. package/dist/types/components/rk-rating/rk-rating.d.ts +26 -0
  96. package/dist/types/components/rk-recipe-card/rk-recipe-card.d.ts +67 -0
  97. package/dist/types/components/rk-search-bar/rk-search-bar.d.ts +39 -0
  98. package/dist/types/components/rk-segmented-control/rk-segmented-control.d.ts +37 -0
  99. package/dist/types/components/rk-tag-input/rk-tag-input.d.ts +35 -0
  100. package/{packages/recipe-kit-stencil/src → dist/types}/components.d.ts +1 -1
  101. package/{packages/recipe-kit-stencil/src/index.ts → dist/types/index.d.ts} +0 -2
  102. package/dist/types/stencil-public-runtime.d.ts +1873 -0
  103. package/loader/cdn.js +1 -0
  104. package/loader/index.cjs.js +1 -0
  105. package/loader/index.d.ts +24 -0
  106. package/loader/index.es2017.js +1 -0
  107. package/loader/index.js +2 -0
  108. package/package.json +84 -22
  109. package/.claude/settings.local.json +0 -10
  110. package/.npmr +0 -1
  111. package/apps/recipe-web/package.json +0 -31
  112. package/apps/recipe-web/src/app.css +0 -418
  113. package/apps/recipe-web/src/app.d.ts +0 -32
  114. package/apps/recipe-web/src/app.html +0 -25
  115. package/apps/recipe-web/src/lib/api/mealdb.ts +0 -240
  116. package/apps/recipe-web/src/lib/components/AppHeader.svelte +0 -290
  117. package/apps/recipe-web/src/lib/components/ConfirmDialog.svelte +0 -46
  118. package/apps/recipe-web/src/lib/components/Modal.svelte +0 -33
  119. package/apps/recipe-web/src/lib/components/RecipeCard.svelte +0 -100
  120. package/apps/recipe-web/src/lib/components/RecipeForm.svelte +0 -560
  121. package/apps/recipe-web/src/lib/components/RecipeGrid.svelte +0 -77
  122. package/apps/recipe-web/src/lib/components/RecipePickerDialog.svelte +0 -185
  123. package/apps/recipe-web/src/lib/components/Toaster.svelte +0 -111
  124. package/apps/recipe-web/src/lib/domain/recipe.ts +0 -130
  125. package/apps/recipe-web/src/lib/domain/validation.ts +0 -189
  126. package/apps/recipe-web/src/lib/stencil/WebComponent.svelte +0 -145
  127. package/apps/recipe-web/src/lib/stencil/register.ts +0 -28
  128. package/apps/recipe-web/src/lib/stores/favorites.svelte.ts +0 -81
  129. package/apps/recipe-web/src/lib/stores/myRecipes.svelte.ts +0 -98
  130. package/apps/recipe-web/src/lib/stores/persisted.svelte.ts +0 -73
  131. package/apps/recipe-web/src/lib/stores/planner.svelte.ts +0 -275
  132. package/apps/recipe-web/src/lib/stores/theme.svelte.ts +0 -67
  133. package/apps/recipe-web/src/lib/stores/toast.svelte.ts +0 -66
  134. package/apps/recipe-web/src/routes/+error.svelte +0 -36
  135. package/apps/recipe-web/src/routes/+layout.svelte +0 -62
  136. package/apps/recipe-web/src/routes/+page.svelte +0 -352
  137. package/apps/recipe-web/src/routes/+page.ts +0 -107
  138. package/apps/recipe-web/src/routes/favorites/+page.svelte +0 -170
  139. package/apps/recipe-web/src/routes/my-recipes/+layout.ts +0 -55
  140. package/apps/recipe-web/src/routes/my-recipes/+page.svelte +0 -180
  141. package/apps/recipe-web/src/routes/my-recipes/[id]/edit/+page.svelte +0 -104
  142. package/apps/recipe-web/src/routes/my-recipes/new/+page.svelte +0 -67
  143. package/apps/recipe-web/src/routes/planner/+page.svelte +0 -422
  144. package/apps/recipe-web/src/routes/recipes/[id]/+page.svelte +0 -538
  145. package/apps/recipe-web/src/routes/recipes/[id]/+page.ts +0 -24
  146. package/apps/recipe-web/static/favicon.svg +0 -8
  147. package/apps/recipe-web/static/robots.txt +0 -2
  148. package/apps/recipe-web/svelte.config.js +0 -35
  149. package/apps/recipe-web/tsconfig.json +0 -14
  150. package/apps/recipe-web/vite.config.ts +0 -23
  151. package/packages/recipe-kit-stencil/README.md +0 -185
  152. package/packages/recipe-kit-stencil/package-lock.json +0 -4772
  153. package/packages/recipe-kit-stencil/package.json +0 -91
  154. package/packages/recipe-kit-stencil/src/components/rk-badge/readme.md +0 -38
  155. package/packages/recipe-kit-stencil/src/components/rk-badge/rk-badge.tsx +0 -31
  156. package/packages/recipe-kit-stencil/src/components/rk-chip/readme.md +0 -52
  157. package/packages/recipe-kit-stencil/src/components/rk-chip/rk-chip.tsx +0 -105
  158. package/packages/recipe-kit-stencil/src/components/rk-empty-state/readme.md +0 -44
  159. package/packages/recipe-kit-stencil/src/components/rk-empty-state/rk-empty-state.tsx +0 -50
  160. package/packages/recipe-kit-stencil/src/components/rk-meal-slot/readme.md +0 -58
  161. package/packages/recipe-kit-stencil/src/components/rk-meal-slot/rk-meal-slot.tsx +0 -182
  162. package/packages/recipe-kit-stencil/src/components/rk-modal/readme.md +0 -57
  163. package/packages/recipe-kit-stencil/src/components/rk-modal/rk-modal.tsx +0 -176
  164. package/packages/recipe-kit-stencil/src/components/rk-rating/readme.md +0 -42
  165. package/packages/recipe-kit-stencil/src/components/rk-rating/rk-rating.tsx +0 -114
  166. package/packages/recipe-kit-stencil/src/components/rk-recipe-card/readme.md +0 -66
  167. package/packages/recipe-kit-stencil/src/components/rk-recipe-card/rk-recipe-card.tsx +0 -179
  168. package/packages/recipe-kit-stencil/src/components/rk-recipe-card/test/rk-recipe-card.spec.tsx +0 -70
  169. package/packages/recipe-kit-stencil/src/components/rk-search-bar/readme.md +0 -67
  170. package/packages/recipe-kit-stencil/src/components/rk-search-bar/rk-search-bar.tsx +0 -152
  171. package/packages/recipe-kit-stencil/src/components/rk-search-bar/test/rk-search-bar.spec.tsx +0 -93
  172. package/packages/recipe-kit-stencil/src/components/rk-segmented-control/readme.md +0 -41
  173. package/packages/recipe-kit-stencil/src/components/rk-segmented-control/rk-segmented-control.tsx +0 -115
  174. package/packages/recipe-kit-stencil/src/components/rk-tag-input/readme.md +0 -47
  175. package/packages/recipe-kit-stencil/src/components/rk-tag-input/rk-tag-input.tsx +0 -181
  176. package/packages/recipe-kit-stencil/src/components/rk-tag-input/test/rk-tag-input.spec.tsx +0 -76
  177. package/packages/recipe-kit-stencil/src/index.html +0 -259
  178. package/packages/recipe-kit-stencil/stencil.config.ts +0 -41
  179. package/packages/recipe-kit-stencil/tsconfig.json +0 -24
  180. /package/{packages/recipe-kit-stencil/LICENSE → LICENSE} +0 -0
  181. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-badge/rk-badge.css +0 -0
  182. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-chip/rk-chip.css +0 -0
  183. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-empty-state/rk-empty-state.css +0 -0
  184. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-meal-slot/rk-meal-slot.css +0 -0
  185. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-modal/rk-modal.css +0 -0
  186. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-rating/rk-rating.css +0 -0
  187. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-recipe-card/rk-recipe-card.css +0 -0
  188. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-search-bar/rk-search-bar.css +0 -0
  189. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-segmented-control/rk-segmented-control.css +0 -0
  190. /package/{packages/recipe-kit-stencil/src → dist/collection}/components/rk-tag-input/rk-tag-input.css +0 -0
  191. /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":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAErG;;;;;;;;;GASG;AAMH,MAAM,OAAO,OAAO;IALpB;QAQE,2BAA2B;QACnB,SAAI,GAAG,KAAK,CAAC;QAErB,2CAA2C;QACnC,YAAO,GAAG,EAAE,CAAC;QAKrB,2BAA2B;QACnB,SAAI,GAAuB,IAAI,CAAC;QAExC,6DAA6D;QACrD,gBAAW,GAAG,IAAI,CAAC;QAKV,cAAS,GAAG,KAAK,CAAC;QAKnC,4EAA4E;QACpE,mBAAc,GAAG,GAAG,EAAE;YAC5B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC;QACvG,CAAC,CAAC;QAoDM,wBAAmB,GAAG,CAAC,KAAoB,EAAE,EAAE;YACrD,IAAI,CAAC,IAAI,CAAC,IAAI;gBAAE,OAAO;YAEvB,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;gBAC/C,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBACxB,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC3C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBACnC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC7C,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAgB,CAAC;gBAE9F,IAAI,KAAK,CAAC,QAAQ,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;oBACvC,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,CAAC;qBAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;oBAC9C,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEM,mBAAc,GAAG,CAAC,KAAiB,EAAE,EAAE;YAC7C,IAAI,CAAC,IAAI,CAAC,WAAW;gBAAE,OAAO;YAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa;gBAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QACrE,CAAC,CAAC;KAgDH;IA9HW,YAAY,CAAC,MAAe,EAAE,OAAgB;QACtD,IAAI,MAAM,KAAK,OAAO;YAAE,OAAO;QAE/B,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,iBAAiB,GAAG,QAAQ,CAAC,aAA4B,CAAC;YAC/D,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC/D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,gEAAgE;YAChE,qBAAqB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACjD,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAClE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACvB,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,EAAE,CAAC;YAClC,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;QACrC,CAAC;IACH,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,cAAc,EAAE,CAAC;IACxB,CAAC;IAED,gBAAgB;QACd,IAAI,IAAI,CAAC,IAAI;YAAE,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED,oBAAoB;QAClB,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClE,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAEO,UAAU,CAAC,MAAe;QAChC,IAAI,OAAO,QAAQ,KAAK,WAAW;YAAE,OAAO;QAC5C,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,2IAA2I,CAAC;QAC7J,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAc,QAAQ,CAAC,CAAC,CAAC;QAClF,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAc,QAAQ,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,IAAI,EAAE,KAAK,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC3G,CAAC;IAEO,UAAU;QAChB,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACzC,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;IACrC,CAAC;IAiCD,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAC,IAAI,mBAAa,MAAM,GAAG,CAAC;QAEnD,OAAO,CACL,EAAC,IAAI;YACH,WAAK,KAAK,EAAC,UAAU,EAAC,IAAI,EAAC,UAAU,EAAC,OAAO,EAAE,IAAI,CAAC,cAAc;gBAChE,WACE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,EACnD,IAAI,EAAC,OAAO,EACZ,IAAI,EAAC,QAAQ,gBACF,MAAM,gBACL,IAAI,CAAC,OAAO,IAAI,QAAQ,EACpC,QAAQ,EAAE,CAAC,CAAC,EACZ,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;oBAE9B,cAAQ,KAAK,EAAC,QAAQ,EAAC,IAAI,EAAC,QAAQ;wBAClC,YAAM,IAAI,EAAC,QAAQ;4BACjB,WAAK,KAAK,EAAC,QAAQ;gCACjB,UAAI,KAAK,EAAC,SAAS,EAAC,IAAI,EAAC,SAAS,IAC/B,IAAI,CAAC,OAAO,CACV;gCACJ,IAAI,CAAC,UAAU,IAAI,SAAG,KAAK,EAAC,YAAY,IAAE,IAAI,CAAC,UAAU,CAAK,CAC3D,CACD;wBAEN,IAAI,CAAC,WAAW,IAAI,CACnB,cAAQ,KAAK,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,QAAQ,gBAAY,cAAc,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;4BAChH,WAAK,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,kBAAc,GAAG,oBAAgB,OAAO,iBAAa,MAAM;gCACnH,YAAM,CAAC,EAAC,sBAAsB,GAAG,CAC7B,CACC,CACV,CACM;oBAET,WAAK,KAAK,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;wBAC3B,eAAQ,CACJ;oBAEN,cAAQ,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,EAAC,QAAQ;wBACzE,YAAM,IAAI,EAAC,QAAQ,EAAC,YAAY,EAAE,IAAI,CAAC,cAAc,GAAI,CAClD,CACL,CACF,CACD,CACR,CAAC;IACJ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACF","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"]}