@gurpinderjitsingh/recipe-ui 0.1.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.
Files changed (65) hide show
  1. package/README.md +38 -0
  2. package/dist/components/app-modal.d.ts +11 -0
  3. package/dist/components/app-toast.d.ts +11 -0
  4. package/dist/components/index.d.ts +35 -0
  5. package/dist/components/meal-plan-day.d.ts +11 -0
  6. package/dist/components/recipe-card.d.ts +11 -0
  7. package/dist/components/recipe-filter-bar.d.ts +11 -0
  8. package/dist/components/recipe-form.d.ts +11 -0
  9. package/dist/components/recipe-rating.d.ts +11 -0
  10. package/dist/components/recipe-search-bar.d.ts +11 -0
  11. package/dist/recipe-ui/app-modal-BL_aVG1A.js +23 -0
  12. package/dist/recipe-ui/app-modal-BL_aVG1A.js.map +1 -0
  13. package/dist/recipe-ui/app-modal.entry.js +5 -0
  14. package/dist/recipe-ui/app-modal.entry.js.map +1 -0
  15. package/dist/recipe-ui/app-toast-DjuZTKj8.js +27 -0
  16. package/dist/recipe-ui/app-toast-DjuZTKj8.js.map +1 -0
  17. package/dist/recipe-ui/app-toast.entry.js +5 -0
  18. package/dist/recipe-ui/app-toast.entry.js.map +1 -0
  19. package/dist/recipe-ui/index-DV5P9Jd8.js +4656 -0
  20. package/dist/recipe-ui/index-DV5P9Jd8.js.map +1 -0
  21. package/dist/recipe-ui/index.esm.js +17 -0
  22. package/dist/recipe-ui/index.esm.js.map +1 -0
  23. package/dist/recipe-ui/meal-plan-day-9rmKiM44.js +23 -0
  24. package/dist/recipe-ui/meal-plan-day-9rmKiM44.js.map +1 -0
  25. package/dist/recipe-ui/meal-plan-day.entry.js +5 -0
  26. package/dist/recipe-ui/meal-plan-day.entry.js.map +1 -0
  27. package/dist/recipe-ui/recipe-card-91fhJAz5.js +28 -0
  28. package/dist/recipe-ui/recipe-card-91fhJAz5.js.map +1 -0
  29. package/dist/recipe-ui/recipe-card.entry.js +5 -0
  30. package/dist/recipe-ui/recipe-card.entry.js.map +1 -0
  31. package/dist/recipe-ui/recipe-filter-bar-DV-B7Ybe.js +25 -0
  32. package/dist/recipe-ui/recipe-filter-bar-DV-B7Ybe.js.map +1 -0
  33. package/dist/recipe-ui/recipe-filter-bar.entry.js +5 -0
  34. package/dist/recipe-ui/recipe-filter-bar.entry.js.map +1 -0
  35. package/dist/recipe-ui/recipe-form-D_DZ8L9Q.js +95 -0
  36. package/dist/recipe-ui/recipe-form-D_DZ8L9Q.js.map +1 -0
  37. package/dist/recipe-ui/recipe-form.entry.js +5 -0
  38. package/dist/recipe-ui/recipe-form.entry.js.map +1 -0
  39. package/dist/recipe-ui/recipe-rating-BZDgwNdK.js +23 -0
  40. package/dist/recipe-ui/recipe-rating-BZDgwNdK.js.map +1 -0
  41. package/dist/recipe-ui/recipe-rating.entry.js +5 -0
  42. package/dist/recipe-ui/recipe-rating.entry.js.map +1 -0
  43. package/dist/recipe-ui/recipe-search-bar-BVbznAHF.js +47 -0
  44. package/dist/recipe-ui/recipe-search-bar-BVbznAHF.js.map +1 -0
  45. package/dist/recipe-ui/recipe-search-bar.entry.js +5 -0
  46. package/dist/recipe-ui/recipe-search-bar.entry.js.map +1 -0
  47. package/dist/recipe-ui/recipe-ui.esm.js +50 -0
  48. package/dist/recipe-ui/recipe-ui.esm.js.map +1 -0
  49. package/dist/types/components/app-modal/app-modal.d.ts +8 -0
  50. package/dist/types/components/app-toast/app-toast.d.ts +10 -0
  51. package/dist/types/components/meal-plan-day/meal-plan-day.d.ts +25 -0
  52. package/dist/types/components/recipe-card/recipe-card.d.ts +26 -0
  53. package/dist/types/components/recipe-filter-bar/recipe-filter-bar.d.ts +16 -0
  54. package/dist/types/components/recipe-form/recipe-form.d.ts +35 -0
  55. package/dist/types/components/recipe-rating/recipe-rating.d.ts +11 -0
  56. package/dist/types/components/recipe-search-bar/recipe-search-bar.d.ts +20 -0
  57. package/dist/types/components.d.ts +620 -0
  58. package/dist/types/index.d.ts +8 -0
  59. package/dist/types/stencil-public-runtime.d.ts +1872 -0
  60. package/loader/cdn.js +1 -0
  61. package/loader/index.cjs.js +1 -0
  62. package/loader/index.d.ts +24 -0
  63. package/loader/index.es2017.js +1 -0
  64. package/loader/index.js +2 -0
  65. package/package.json +38 -0
@@ -0,0 +1,17 @@
1
+ export { R as RecipeCard } from './recipe-card-91fhJAz5.js';
2
+ export { R as RecipeSearchBar } from './recipe-search-bar-BVbznAHF.js';
3
+ export { R as RecipeFilterBar } from './recipe-filter-bar-DV-B7Ybe.js';
4
+ export { R as RecipeRating } from './recipe-rating-BZDgwNdK.js';
5
+ export { R as RecipeForm } from './recipe-form-D_DZ8L9Q.js';
6
+ export { M as MealPlanDay } from './meal-plan-day-9rmKiM44.js';
7
+ export { A as AppModal } from './app-modal-BL_aVG1A.js';
8
+ export { A as AppToast } from './app-toast-DjuZTKj8.js';
9
+ import './index-DV5P9Jd8.js';
10
+
11
+ // Barrel export of all component classes. Not required for the actual
12
+ // dist-custom-elements build (Stencil discovers @Component-decorated
13
+ // classes automatically via stencil.config.ts), but useful if a consumer
14
+ // ever wants typed access to the component classes themselves.
15
+ //# sourceMappingURL=index.esm.js.map
16
+
17
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["src/index.ts"],"sourcesContent":["// Barrel export of all component classes. Not required for the actual\n// dist-custom-elements build (Stencil discovers @Component-decorated\n// classes automatically via stencil.config.ts), but useful if a consumer\n// ever wants typed access to the component classes themselves.\nexport { RecipeCard } from './components/recipe-card/recipe-card';\nexport { RecipeSearchBar } from './components/recipe-search-bar/recipe-search-bar';\nexport { RecipeFilterBar } from './components/recipe-filter-bar/recipe-filter-bar';\nexport { RecipeRating } from './components/recipe-rating/recipe-rating';\nexport { RecipeForm } from './components/recipe-form/recipe-form';\nexport { MealPlanDay } from './components/meal-plan-day/meal-plan-day';\nexport { AppModal } from './components/app-modal/app-modal';\nexport { AppToast } from './components/app-toast/app-toast';\n"],"names":[],"mappings":";;;;;;;;;;AAAA;AACA;AACA;AACA"}
@@ -0,0 +1,23 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const mealPlanDayCss = () => `:host{display:block;font-family:system-ui, sans-serif}.day-col{border:1px solid #eee;border-radius:10px;padding:10px;min-width:150px;display:flex;flex-direction:column;gap:8px;background:#fafafa}h3{margin:0;font-size:13px;text-transform:uppercase;letter-spacing:0.04em;color:#555}.meals{display:flex;flex-direction:column;gap:6px;min-height:40px}.meal-chip{display:flex;align-items:center;gap:6px;background:#fff;border:1px solid #eee;border-radius:8px;padding:4px 6px;font-size:12px}.meal-chip img{width:24px;height:24px;border-radius:4px;object-fit:cover}.meal-chip .title{flex:1}.meal-chip button{border:none;background:none;cursor:pointer;color:#999}.empty{font-size:12px;color:#aaa;margin:4px 0}.add-btn{border:1px dashed #ccc;background:none;border-radius:8px;padding:6px;font-size:12px;cursor:pointer;color:#666}`;
4
+
5
+ const MealPlanDay = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.assignMeal = createEvent(this, "assignMeal", 7);
9
+ this.removeMeal = createEvent(this, "removeMeal", 7);
10
+ this.day = '';
11
+ this.meals = [];
12
+ }
13
+ render() {
14
+ const meals = Array.isArray(this.meals) ? this.meals : [];
15
+ return (h("div", { key: '7a0b592fd4b8d9971d7f4912cecb5f9126be11b3', class: "day-col" }, h("h3", { key: 'b0f1f6a717ad409e6339219ea2f71b73d73bb1f3' }, this.day), h("div", { key: '2c978721af3aad27042572dd7825d5f2b5b659fb', class: "meals" }, meals.map((m) => (h("div", { class: "meal-chip" }, m.image && h("img", { src: m.image, alt: m.title }), h("span", { class: "title" }, m.title), h("button", { onClick: () => this.removeMeal.emit({ day: this.day, mealId: m.id }), "aria-label": "Remove meal" }, "\u2715")))), meals.length === 0 && h("p", { key: '326b5f3201df2416f43debc7257918a60a6ab6dd', class: "empty" }, "No meals planned")), h("button", { key: '3532de633a1f6cb83bb70f91a2df8fe1a9fcbc14', class: "add-btn", onClick: () => this.assignMeal.emit({ day: this.day }) }, "+ Add meal")));
16
+ }
17
+ };
18
+ MealPlanDay.style = mealPlanDayCss();
19
+
20
+ export { MealPlanDay as M };
21
+ //# sourceMappingURL=meal-plan-day-9rmKiM44.js.map
22
+
23
+ //# sourceMappingURL=meal-plan-day-9rmKiM44.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meal-plan-day-9rmKiM44.js","sources":["src/components/meal-plan-day/meal-plan-day.css?tag=meal-plan-day&encapsulation=shadow","src/components/meal-plan-day/meal-plan-day.tsx"],"sourcesContent":[":host { display: block; font-family: system-ui, sans-serif; }\n.day-col {\n border: 1px solid #eee; border-radius: 10px; padding: 10px; min-width: 150px;\n display: flex; flex-direction: column; gap: 8px; background: #fafafa;\n}\nh3 { margin: 0; font-size: 13px; text-transform: uppercase; letter-spacing: 0.04em; color: #555; }\n.meals { display: flex; flex-direction: column; gap: 6px; min-height: 40px; }\n.meal-chip {\n display: flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #eee;\n border-radius: 8px; padding: 4px 6px; font-size: 12px;\n}\n.meal-chip img { width: 24px; height: 24px; border-radius: 4px; object-fit: cover; }\n.meal-chip .title { flex: 1; }\n.meal-chip button { border: none; background: none; cursor: pointer; color: #999; }\n.empty { font-size: 12px; color: #aaa; margin: 4px 0; }\n.add-btn {\n border: 1px dashed #ccc; background: none; border-radius: 8px; padding: 6px;\n font-size: 12px; cursor: pointer; color: #666;\n}\n","import { Component, Prop, Event, EventEmitter, h } from '@stencil/core';\n\nexport interface PlannedMeal {\n id: string;\n title: string;\n image?: string;\n}\n\n/**\n * One column of the weekly meal planner, representing a single day.\n * `meals` is the list of recipes currently assigned to this day.\n * Emits `removeMeal` when a meal's remove button is clicked, and\n * `assignMeal` when the \"+ Add meal\" button is clicked (the parent app\n * is expected to open its own picker/modal and then update `meals`).\n */\n@Component({\n tag: 'meal-plan-day',\n styleUrl: 'meal-plan-day.css',\n shadow: true,\n})\nexport class MealPlanDay {\n @Prop() day: string = '';\n @Prop() meals: PlannedMeal[] = [];\n\n @Event() assignMeal: EventEmitter<{ day: string }>;\n @Event() removeMeal: EventEmitter<{ day: string; mealId: string }>;\n\n render() {\n const meals = Array.isArray(this.meals) ? this.meals : [];\n return (\n <div class=\"day-col\">\n <h3>{this.day}</h3>\n <div class=\"meals\">\n {meals.map((m) => (\n <div class=\"meal-chip\">\n {m.image && <img src={m.image} alt={m.title} />}\n <span class=\"title\">{m.title}</span>\n <button\n onClick={() => this.removeMeal.emit({ day: this.day, mealId: m.id })}\n aria-label=\"Remove meal\"\n >\n ✕\n </button>\n </div>\n ))}\n {meals.length === 0 && <p class=\"empty\">No meals planned</p>}\n </div>\n <button class=\"add-btn\" onClick={() => this.assignMeal.emit({ day: this.day })}>\n + Add meal\n </button>\n </div>\n );\n }\n}\n"],"names":[],"mappings":";;AAAA,MAAM,cAAc,GAAG,MAAM,CAAC,mzBAAmzB,CAAC;;MCoBr0B,WAAW,GAAA,MAAA;AALxB,IAAA,WAAA,CAAA,OAAA,EAAA;;;;AAMU,QAAA,IAAA,CAAA,GAAG,GAAW,EAAE;AAChB,QAAA,IAAA,CAAA,KAAK,GAAkB,EAAE;AA+BlC;IA1BC,MAAM,GAAA;QACJ,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE;QACzD,QACE,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,SAAS,EAAA,EAClB,CAAA,CAAA,IAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAA,EAAK,IAAI,CAAC,GAAG,CAAM,EACnB,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,OAAO,EAAA,EACf,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MACX,CAAA,CAAA,KAAA,EAAA,EAAK,KAAK,EAAC,WAAW,EAAA,EACnB,CAAC,CAAC,KAAK,IAAI,CAAA,CAAA,KAAA,EAAA,EAAK,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAA,CAAI,EAC/C,CAAA,CAAA,MAAA,EAAA,EAAM,KAAK,EAAC,OAAO,IAAE,CAAC,CAAC,KAAK,CAAQ,EACpC,CAAA,CAAA,QAAA,EAAA,EACE,OAAO,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,EAAA,YAAA,EACzD,aAAa,EAAA,EAAA,QAAA,CAGjB,CACL,CACP,CAAC,EACD,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,0DAAG,KAAK,EAAC,OAAO,EAAA,EAAA,kBAAA,CAAqB,CACxD,EACN,CAAA,CAAA,QAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAQ,KAAK,EAAC,SAAS,EAAC,OAAO,EAAE,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,EAAA,EAAA,YAAA,CAErE,CACL;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ export { M as meal_plan_day } from './meal-plan-day-9rmKiM44.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=meal-plan-day.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=meal-plan-day.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"meal-plan-day.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,28 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const recipeCardCss = () => `:host{display:block;font-family:system-ui, sans-serif}.card{border:1px solid #e5e5e5;border-radius:12px;overflow:hidden;cursor:pointer;background:#fff;transition:box-shadow 0.15s ease, transform 0.15s ease}.card:hover{box-shadow:0 4px 16px rgba(0, 0, 0, 0.08);transform:translateY(-2px)}.image-wrap{position:relative;aspect-ratio:4 / 3;background:#f2f2f2}.image-wrap img{width:100%;height:100%;object-fit:cover;display:block}.placeholder{width:100%;height:100%;background:linear-gradient(135deg, #eee, #ddd)}.fav-btn{position:absolute;top:8px;right:8px;border:none;background:rgba(255, 255, 255, 0.9);border-radius:50%;width:32px;height:32px;font-size:16px;cursor:pointer;color:#f5a623}.fav-btn.active{color:#ff6b35}.body{padding:12px 14px}h3{margin:0 0 4px;font-size:15px;line-height:1.3}.category{font-size:12px;color:#777;text-transform:uppercase;letter-spacing:0.03em}.actions{margin-top:8px}`;
4
+
5
+ const RecipeCard = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.favoriteToggle = createEvent(this, "favoriteToggle", 7);
9
+ this.cardClick = createEvent(this, "cardClick", 7);
10
+ /** Category / cuisine label */
11
+ this.category = '';
12
+ /** Whether this recipe is currently favorited */
13
+ this.favorite = false;
14
+ this.onFavoriteClick = (e) => {
15
+ e.stopPropagation();
16
+ this.favoriteToggle.emit({ favorite: !this.favorite });
17
+ };
18
+ }
19
+ render() {
20
+ return (h("div", { key: 'abd19b1d63409b52cf66f348131f5f6f77bb47a6', class: "card", onClick: () => this.cardClick.emit() }, h("div", { key: '1292c3f6dfe7d3b399943004f764c41a3d072188', class: "image-wrap" }, this.image ? h("img", { src: this.image, alt: this.recipeTitle }) : h("div", { class: "placeholder" }), h("button", { key: '0efdc01af349d011ca0e11c4df5438b79876b467', class: { 'fav-btn': true, active: this.favorite }, onClick: this.onFavoriteClick, "aria-label": "Toggle favorite" }, this.favorite ? '★' : '☆')), h("div", { key: '66c2c4d845d9cc082c07f24f5d827313f6ad51c4', class: "body" }, h("h3", { key: '779fc629c3b97b6158f99440d9bb17fafb708dd3' }, this.recipeTitle), this.category && h("span", { key: '259ec599a43a5c8a279ad5993ba36f8602125b76', class: "category" }, this.category), h("div", { key: '58ab20f9682b4b73b7d027571496641cbfa76d0b', class: "actions" }, h("slot", { key: '28103422d993e58b133c2350e37c06fddf24300f', name: "actions" })))));
21
+ }
22
+ };
23
+ RecipeCard.style = recipeCardCss();
24
+
25
+ export { RecipeCard as R };
26
+ //# sourceMappingURL=recipe-card-91fhJAz5.js.map
27
+
28
+ //# sourceMappingURL=recipe-card-91fhJAz5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-card-91fhJAz5.js","sources":["src/components/recipe-card/recipe-card.css?tag=recipe-card&encapsulation=shadow","src/components/recipe-card/recipe-card.tsx"],"sourcesContent":[":host { display: block; font-family: system-ui, sans-serif; }\n.card {\n border: 1px solid #e5e5e5;\n border-radius: 12px;\n overflow: hidden;\n cursor: pointer;\n background: #fff;\n transition: box-shadow 0.15s ease, transform 0.15s ease;\n}\n.card:hover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); transform: translateY(-2px); }\n.image-wrap { position: relative; aspect-ratio: 4 / 3; background: #f2f2f2; }\n.image-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.placeholder { width: 100%; height: 100%; background: linear-gradient(135deg, #eee, #ddd); }\n.fav-btn {\n position: absolute; top: 8px; right: 8px; border: none;\n background: rgba(255, 255, 255, 0.9); border-radius: 50%;\n width: 32px; height: 32px; font-size: 16px; cursor: pointer; color: #f5a623;\n}\n.fav-btn.active { color: #ff6b35; }\n.body { padding: 12px 14px; }\nh3 { margin: 0 0 4px; font-size: 15px; line-height: 1.3; }\n.category { font-size: 12px; color: #777; text-transform: uppercase; letter-spacing: 0.03em; }\n.actions { margin-top: 8px; }\n","import { Component, Prop, Event, EventEmitter, h } from '@stencil/core';\n\n/**\n * Displays a summary of a recipe (image, title, category) inside a card.\n * Emits `cardClick` when the card body is clicked and `favoriteToggle`\n * when the favorite button is pressed. Accepts a named `actions` slot\n * for extra buttons supplied by the consuming app.\n */\n@Component({\n tag: 'recipe-card',\n styleUrl: 'recipe-card.css',\n shadow: true,\n})\nexport class RecipeCard {\n /** Recipe title (named recipeTitle, not title, to avoid colliding with\n * the native HTML title/tooltip attribute every element already has) */\n @Prop() recipeTitle: string;\n /** Image URL */\n @Prop() image: string;\n /** Category / cuisine label */\n @Prop() category: string = '';\n /** Whether this recipe is currently favorited */\n @Prop() favorite: boolean = false;\n\n /** Fired when the favorite button is toggled */\n @Event() favoriteToggle: EventEmitter<{ favorite: boolean }>;\n /** Fired when the card (excluding the favorite button) is clicked */\n @Event() cardClick: EventEmitter<void>;\n\n private onFavoriteClick = (e: MouseEvent) => {\n e.stopPropagation();\n this.favoriteToggle.emit({ favorite: !this.favorite });\n };\n\n render() {\n return (\n <div class=\"card\" onClick={() => this.cardClick.emit()}>\n <div class=\"image-wrap\">\n {this.image ? <img src={this.image} alt={this.recipeTitle} /> : <div class=\"placeholder\" />}\n <button\n class={{ 'fav-btn': true, active: this.favorite }}\n onClick={this.onFavoriteClick}\n aria-label=\"Toggle favorite\"\n >\n {this.favorite ? '★' : '☆'}\n </button>\n </div>\n <div class=\"body\">\n <h3>{this.recipeTitle}</h3>\n {this.category && <span class=\"category\">{this.category}</span>}\n <div class=\"actions\">\n <slot name=\"actions\"></slot>\n </div>\n </div>\n </div>\n );\n }\n}\n"],"names":[],"mappings":";;AAAA,MAAM,aAAa,GAAG,MAAM,CAAC,g4BAAg4B,CAAC;;MCaj5B,UAAU,GAAA,MAAA;AALvB,IAAA,WAAA,CAAA,OAAA,EAAA;;;;;AAYU,QAAA,IAAA,CAAA,QAAQ,GAAW,EAAE;;AAErB,QAAA,IAAA,CAAA,QAAQ,GAAY,KAAK;AAOzB,QAAA,IAAA,CAAA,eAAe,GAAG,CAAC,CAAa,KAAI;YAC1C,CAAC,CAAC,eAAe,EAAE;AACnB,YAAA,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;AACxD,SAAC;AAyBF;IAvBC,MAAM,GAAA;QACJ,QACE,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,MAAM,EAAC,OAAO,EAAE,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,EAAA,EACpD,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,YAAY,EAAA,EACpB,IAAI,CAAC,KAAK,GAAG,WAAK,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,EAAA,CAAI,GAAG,CAAA,CAAA,KAAA,EAAA,EAAK,KAAK,EAAC,aAAa,EAAA,CAAG,EAC3F,CAAA,CAAA,QAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EACE,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,EACjD,OAAO,EAAE,IAAI,CAAC,eAAe,EAAA,YAAA,EAClB,iBAAiB,EAAA,EAE3B,IAAI,CAAC,QAAQ,GAAG,GAAG,GAAG,GAAG,CACnB,CACL,EACN,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,MAAM,EAAA,EACf,CAAA,CAAA,IAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAA,EAAK,IAAI,CAAC,WAAW,CAAM,EAC1B,IAAI,CAAC,QAAQ,IAAI,CAAA,CAAA,MAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAM,KAAK,EAAC,UAAU,EAAA,EAAE,IAAI,CAAC,QAAQ,CAAQ,EAC/D,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,SAAS,EAAA,EAClB,CAAA,CAAA,MAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAM,IAAI,EAAC,SAAS,EAAA,CAAQ,CACxB,CACF,CACF;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ export { R as recipe_card } from './recipe-card-91fhJAz5.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=recipe-card.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=recipe-card.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"recipe-card.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,25 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const recipeFilterBarCss = () => `:host{display:block;font-family:system-ui, sans-serif}.wrap{display:flex;flex-wrap:wrap;gap:8px}.chip{border:1px solid #ddd;background:#fff;border-radius:999px;padding:6px 14px;font-size:13px;cursor:pointer;color:#444}.chip.active{background:#ff6b35;border-color:#ff6b35;color:#fff}`;
4
+
5
+ const RecipeFilterBar = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.filterChange = createEvent(this, "filterChange", 7);
9
+ this.categories = [];
10
+ this.active = '';
11
+ }
12
+ select(value) {
13
+ this.filterChange.emit({ value });
14
+ }
15
+ render() {
16
+ const cats = Array.isArray(this.categories) ? this.categories : [];
17
+ return (h("div", { key: '13bc63f39ebfc55c0f05c035b63036121f7b3930', class: "wrap" }, h("button", { key: '8aeb99775268204433a8f9831eca9873066c7b9e', class: { chip: true, active: this.active === '' }, onClick: () => this.select('') }, "All"), cats.map((c) => (h("button", { class: { chip: true, active: this.active === c }, onClick: () => this.select(c) }, c)))));
18
+ }
19
+ };
20
+ RecipeFilterBar.style = recipeFilterBarCss();
21
+
22
+ export { RecipeFilterBar as R };
23
+ //# sourceMappingURL=recipe-filter-bar-DV-B7Ybe.js.map
24
+
25
+ //# sourceMappingURL=recipe-filter-bar-DV-B7Ybe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-filter-bar-DV-B7Ybe.js","sources":["src/components/recipe-filter-bar/recipe-filter-bar.css?tag=recipe-filter-bar&encapsulation=shadow","src/components/recipe-filter-bar/recipe-filter-bar.tsx"],"sourcesContent":[":host { display: block; font-family: system-ui, sans-serif; }\n.wrap { display: flex; flex-wrap: wrap; gap: 8px; }\n.chip {\n border: 1px solid #ddd; background: #fff; border-radius: 999px;\n padding: 6px 14px; font-size: 13px; cursor: pointer; color: #444;\n}\n.chip.active { background: #ff6b35; border-color: #ff6b35; color: #fff; }\n","import { Component, Prop, Event, EventEmitter, h } from '@stencil/core';\n\n/**\n * Renders a row of selectable filter chips (e.g. categories/cuisines).\n * `categories` accepts either a JSON string array or an actual array\n * (Stencil auto-parses complex props passed as attributes).\n * Emits `filterChange` with the selected value ('' = all).\n */\n@Component({\n tag: 'recipe-filter-bar',\n styleUrl: 'recipe-filter-bar.css',\n shadow: true,\n})\nexport class RecipeFilterBar {\n @Prop() categories: string[] = [];\n @Prop() active: string = '';\n\n @Event() filterChange: EventEmitter<{ value: string }>;\n\n private select(value: string) {\n this.filterChange.emit({ value });\n }\n\n render() {\n const cats = Array.isArray(this.categories) ? this.categories : [];\n return (\n <div class=\"wrap\">\n <button class={{ chip: true, active: this.active === '' }} onClick={() => this.select('')}>\n All\n </button>\n {cats.map((c) => (\n <button class={{ chip: true, active: this.active === c }} onClick={() => this.select(c)}>\n {c}\n </button>\n ))}\n </div>\n );\n }\n}\n"],"names":[],"mappings":";;AAAA,MAAM,kBAAkB,GAAG,MAAM,CAAC,0RAA0R,CAAC;;MCahT,eAAe,GAAA,MAAA;AAL5B,IAAA,WAAA,CAAA,OAAA,EAAA;;;AAMU,QAAA,IAAA,CAAA,UAAU,GAAa,EAAE;AACzB,QAAA,IAAA,CAAA,MAAM,GAAW,EAAE;AAuB5B;AAnBS,IAAA,MAAM,CAAC,KAAa,EAAA;QAC1B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;;IAGnC,MAAM,GAAA;QACJ,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,UAAU,GAAG,EAAE;QAClE,QACE,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,MAAM,EAAA,EACf,CAAA,CAAA,QAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAQ,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAA,EAAA,KAAA,CAEhF,EACR,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MACV,CAAA,CAAA,QAAA,EAAA,EAAQ,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IACpF,CAAC,CACK,CACV,CAAC,CACE;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ export { R as recipe_filter_bar } from './recipe-filter-bar-DV-B7Ybe.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=recipe-filter-bar.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=recipe-filter-bar.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"recipe-filter-bar.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,95 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const recipeFormCss = () => `:host{display:block;font-family:system-ui, sans-serif}form{display:flex;flex-direction:column;gap:14px}label{display:flex;flex-direction:column;gap:4px;font-size:13px;color:#333;font-weight:600}input,textarea{font-weight:400;padding:8px 10px;border:1px solid #ddd;border-radius:6px;font-size:14px;font-family:inherit}.field{display:flex;flex-direction:column;gap:6px;font-size:13px;font-weight:600}.ingredient-row{display:flex;gap:6px}.ingredient-row input{flex:1}.ingredient-row button{border:none;background:#f4f4f4;border-radius:6px;cursor:pointer;width:30px}.add-btn{align-self:flex-start;border:1px dashed #bbb;background:none;border-radius:6px;padding:6px 10px;cursor:pointer;font-size:13px;color:#555}.error{color:#e74c3c;font-size:12px;font-weight:400}.footer{display:flex;justify-content:flex-end;gap:10px;margin-top:6px}.cancel-btn{border:1px solid #ddd;background:#fff;border-radius:8px;padding:8px 16px;cursor:pointer}.save-btn{border:none;background:#ff6b35;color:#fff;border-radius:8px;padding:8px 18px;cursor:pointer;font-weight:600}`;
4
+
5
+ const RecipeForm = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.save = createEvent(this, "save", 7);
9
+ this.cancel = createEvent(this, "cancel", 7);
10
+ this.title = '';
11
+ this.image = '';
12
+ this.category = '';
13
+ this.ingredients = [''];
14
+ this.instructions = '';
15
+ this.errors = {};
16
+ this.onSubmit = (e) => {
17
+ e.preventDefault();
18
+ if (!this.validate())
19
+ return;
20
+ const data = {
21
+ id: this.initialData?.id,
22
+ title: this.title.trim(),
23
+ image: this.image.trim(),
24
+ category: this.category.trim(),
25
+ ingredients: this.ingredients.map((i) => i.trim()).filter(Boolean),
26
+ instructions: this.instructions.trim(),
27
+ };
28
+ this.save.emit(data);
29
+ };
30
+ this.addIngredient = () => {
31
+ this.ingredients = [...this.ingredients, ''];
32
+ };
33
+ this.removeIngredient = (index) => {
34
+ this.ingredients = this.ingredients.filter((_, i) => i !== index);
35
+ };
36
+ }
37
+ componentWillLoad() {
38
+ this.loadInitialData();
39
+ }
40
+ onInitialDataChange() {
41
+ this.loadInitialData();
42
+ }
43
+ loadInitialData() {
44
+ let data;
45
+ if (typeof this.initialData === 'string' && this.initialData) {
46
+ try {
47
+ data = JSON.parse(this.initialData);
48
+ }
49
+ catch {
50
+ data = undefined;
51
+ }
52
+ }
53
+ else if (this.initialData && typeof this.initialData === 'object') {
54
+ data = this.initialData;
55
+ }
56
+ if (data) {
57
+ this.title = data.title ?? '';
58
+ this.image = data.image ?? '';
59
+ this.category = data.category ?? '';
60
+ this.ingredients = data.ingredients?.length ? [...data.ingredients] : [''];
61
+ this.instructions = data.instructions ?? '';
62
+ }
63
+ }
64
+ validate() {
65
+ const errors = {};
66
+ if (!this.title.trim())
67
+ errors.title = 'Title is required.';
68
+ const cleanIngredients = this.ingredients.map((i) => i.trim()).filter(Boolean);
69
+ if (cleanIngredients.length === 0)
70
+ errors.ingredients = 'Add at least one ingredient.';
71
+ if (!this.instructions.trim())
72
+ errors.instructions = 'Instructions are required.';
73
+ this.errors = errors;
74
+ return Object.keys(errors).length === 0;
75
+ }
76
+ updateIngredient(index, value) {
77
+ const next = [...this.ingredients];
78
+ next[index] = value;
79
+ this.ingredients = next;
80
+ }
81
+ render() {
82
+ return (h("form", { key: 'bb5c6c7f1b421b55515994f0256c5b39ea781632', onSubmit: this.onSubmit }, h("slot", { key: '16940e4c8368aa6b546254ff8db7b28885a4409b', name: "header" }), h("label", { key: '7e3d6ba3231039ecdcca6a4c4314cf27038e1732' }, "Title", h("input", { key: 'f86056de021f040b8bbf3bf67a0d40ba526ad754', type: "text", value: this.title, onInput: (e) => (this.title = e.target.value) }), this.errors.title && h("span", { key: '54d552a15e037f54b2c6f0fd13cb4b685c25c76c', class: "error" }, this.errors.title)), h("label", { key: '376da4904e7919b5540ee8dabf494f6aab053523' }, "Image URL", h("input", { key: '761b64f0e5153ca0e2bf61a7db4c4106ae2f79ef', type: "text", value: this.image, onInput: (e) => (this.image = e.target.value) })), h("label", { key: '0b7ff19b8718540b08921bb7f301be5cacf0df36' }, "Category", h("input", { key: '4f1bae8316d588e93006a12e0d824579ddbece43', type: "text", value: this.category, onInput: (e) => (this.category = e.target.value) })), h("div", { key: '280d98b91621defa547dbcc35c0cb6da55a67b5f', class: "field" }, h("span", { key: '96a015e055b434441eba85dcb89470823d0ebcc8' }, "Ingredients"), this.ingredients.map((ing, i) => (h("div", { class: "ingredient-row" }, h("input", { type: "text", value: ing, onInput: (e) => this.updateIngredient(i, e.target.value) }), h("button", { type: "button", onClick: () => this.removeIngredient(i), "aria-label": "Remove" }, "\u2715")))), h("button", { key: '697cda22eb5bdf42e50ac40669619fbc04140405', type: "button", class: "add-btn", onClick: this.addIngredient }, "+ Add ingredient"), this.errors.ingredients && h("span", { key: '6a46c393bdd9af8e1e1255792d5532daf433f25b', class: "error" }, this.errors.ingredients)), h("label", { key: '6abdbe72d381c3a0a56c46560a92b1e533fcce2a' }, "Instructions", h("textarea", { key: '1d3b62d6543e42a4cd0755522f657c13e2d1e4a7', rows: 5, onInput: (e) => (this.instructions = e.target.value) }, this.instructions), this.errors.instructions && h("span", { key: 'b5845bb5cd0ddd40de21f074307513d376a05f56', class: "error" }, this.errors.instructions)), h("div", { key: '778adeff176e2d2b080aad59602db6e92a69ea1c', class: "footer" }, h("slot", { key: 'c9584d6606ed33f616eb8f3035aa4a0fd5086af3', name: "footer" }), h("button", { key: 'b2e17dfb528996a1e6a8a1f8aac36b36e3b07c3a', type: "button", class: "cancel-btn", onClick: () => this.cancel.emit() }, "Cancel"), h("button", { key: 'a9e91907aa44c8587a2c492f869d0cb5daa942fe', type: "submit", class: "save-btn" }, "Save Recipe"))));
83
+ }
84
+ static get watchers() { return {
85
+ "initialData": [{
86
+ "onInitialDataChange": 0
87
+ }]
88
+ }; }
89
+ };
90
+ RecipeForm.style = recipeFormCss();
91
+
92
+ export { RecipeForm as R };
93
+ //# sourceMappingURL=recipe-form-D_DZ8L9Q.js.map
94
+
95
+ //# sourceMappingURL=recipe-form-D_DZ8L9Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-form-D_DZ8L9Q.js","sources":["src/components/recipe-form/recipe-form.css?tag=recipe-form&encapsulation=shadow","src/components/recipe-form/recipe-form.tsx"],"sourcesContent":[":host { display: block; font-family: system-ui, sans-serif; }\nform { display: flex; flex-direction: column; gap: 14px; }\nlabel { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: #333; font-weight: 600; }\ninput, textarea {\n font-weight: 400; padding: 8px 10px; border: 1px solid #ddd; border-radius: 6px;\n font-size: 14px; font-family: inherit;\n}\n.field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; font-weight: 600; }\n.ingredient-row { display: flex; gap: 6px; }\n.ingredient-row input { flex: 1; }\n.ingredient-row button { border: none; background: #f4f4f4; border-radius: 6px; cursor: pointer; width: 30px; }\n.add-btn {\n align-self: flex-start; border: 1px dashed #bbb; background: none; border-radius: 6px;\n padding: 6px 10px; cursor: pointer; font-size: 13px; color: #555;\n}\n.error { color: #e74c3c; font-size: 12px; font-weight: 400; }\n.footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 6px; }\n.cancel-btn { border: 1px solid #ddd; background: #fff; border-radius: 8px; padding: 8px 16px; cursor: pointer; }\n.save-btn { border: none; background: #ff6b35; color: #fff; border-radius: 8px; padding: 8px 18px; cursor: pointer; font-weight: 600; }\n","import { Component, Prop, State, Event, EventEmitter, h, Watch } from '@stencil/core';\n\nexport interface RecipeFormData {\n id?: string;\n title: string;\n image: string;\n category: string;\n ingredients: string[];\n instructions: string;\n}\n\n/**\n * Add/edit form for user-created recipes. Performs client-side validation\n * (title required, at least one non-empty ingredient, instructions required)\n * and only emits `save` once the data is valid.\n */\n@Component({\n tag: 'recipe-form',\n styleUrl: 'recipe-form.css',\n shadow: true,\n})\nexport class RecipeForm {\n /** Pass an existing recipe (as a JSON string or object) to edit it */\n @Prop() initialData: RecipeFormData | string;\n\n @State() title = '';\n @State() image = '';\n @State() category = '';\n @State() ingredients: string[] = [''];\n @State() instructions = '';\n @State() errors: Record<string, string> = {};\n\n @Event() save: EventEmitter<RecipeFormData>;\n @Event() cancel: EventEmitter<void>;\n\n componentWillLoad() {\n this.loadInitialData();\n }\n\n @Watch('initialData')\n onInitialDataChange() {\n this.loadInitialData();\n }\n\n private loadInitialData() {\n let data: RecipeFormData | undefined;\n if (typeof this.initialData === 'string' && this.initialData) {\n try {\n data = JSON.parse(this.initialData);\n } catch {\n data = undefined;\n }\n } else if (this.initialData && typeof this.initialData === 'object') {\n data = this.initialData as RecipeFormData;\n }\n if (data) {\n this.title = data.title ?? '';\n this.image = data.image ?? '';\n this.category = data.category ?? '';\n this.ingredients = data.ingredients?.length ? [...data.ingredients] : [''];\n this.instructions = data.instructions ?? '';\n }\n }\n\n private validate(): boolean {\n const errors: Record<string, string> = {};\n if (!this.title.trim()) errors.title = 'Title is required.';\n const cleanIngredients = this.ingredients.map((i) => i.trim()).filter(Boolean);\n if (cleanIngredients.length === 0) errors.ingredients = 'Add at least one ingredient.';\n if (!this.instructions.trim()) errors.instructions = 'Instructions are required.';\n this.errors = errors;\n return Object.keys(errors).length === 0;\n }\n\n private onSubmit = (e: Event) => {\n e.preventDefault();\n if (!this.validate()) return;\n const data: RecipeFormData = {\n id: (this.initialData as RecipeFormData)?.id,\n title: this.title.trim(),\n image: this.image.trim(),\n category: this.category.trim(),\n ingredients: this.ingredients.map((i) => i.trim()).filter(Boolean),\n instructions: this.instructions.trim(),\n };\n this.save.emit(data);\n };\n\n private updateIngredient(index: number, value: string) {\n const next = [...this.ingredients];\n next[index] = value;\n this.ingredients = next;\n }\n\n private addIngredient = () => {\n this.ingredients = [...this.ingredients, ''];\n };\n\n private removeIngredient = (index: number) => {\n this.ingredients = this.ingredients.filter((_, i) => i !== index);\n };\n\n render() {\n return (\n <form onSubmit={this.onSubmit}>\n <slot name=\"header\"></slot>\n\n <label>\n Title\n <input\n type=\"text\"\n value={this.title}\n onInput={(e) => (this.title = (e.target as HTMLInputElement).value)}\n />\n {this.errors.title && <span class=\"error\">{this.errors.title}</span>}\n </label>\n\n <label>\n Image URL\n <input\n type=\"text\"\n value={this.image}\n onInput={(e) => (this.image = (e.target as HTMLInputElement).value)}\n />\n </label>\n\n <label>\n Category\n <input\n type=\"text\"\n value={this.category}\n onInput={(e) => (this.category = (e.target as HTMLInputElement).value)}\n />\n </label>\n\n <div class=\"field\">\n <span>Ingredients</span>\n {this.ingredients.map((ing, i) => (\n <div class=\"ingredient-row\">\n <input\n type=\"text\"\n value={ing}\n onInput={(e) => this.updateIngredient(i, (e.target as HTMLInputElement).value)}\n />\n <button type=\"button\" onClick={() => this.removeIngredient(i)} aria-label=\"Remove\">\n ✕\n </button>\n </div>\n ))}\n <button type=\"button\" class=\"add-btn\" onClick={this.addIngredient}>\n + Add ingredient\n </button>\n {this.errors.ingredients && <span class=\"error\">{this.errors.ingredients}</span>}\n </div>\n\n <label>\n Instructions\n <textarea\n rows={5}\n onInput={(e) => (this.instructions = (e.target as HTMLTextAreaElement).value)}\n >\n {this.instructions}\n </textarea>\n {this.errors.instructions && <span class=\"error\">{this.errors.instructions}</span>}\n </label>\n\n <div class=\"footer\">\n <slot name=\"footer\"></slot>\n <button type=\"button\" class=\"cancel-btn\" onClick={() => this.cancel.emit()}>\n Cancel\n </button>\n <button type=\"submit\" class=\"save-btn\">\n Save Recipe\n </button>\n </div>\n </form>\n );\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { R as recipe_form } from './recipe-form-D_DZ8L9Q.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=recipe-form.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=recipe-form.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"recipe-form.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,23 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const recipeRatingCss = () => `:host{display:inline-block;font-family:system-ui, sans-serif}.wrap{display:flex;gap:2px}.star{font-size:18px;color:#ddd}.star.filled{color:#f5a623}.star.clickable{cursor:pointer}`;
4
+
5
+ const RecipeRating = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.ratingChange = createEvent(this, "ratingChange", 7);
9
+ this.value = 0;
10
+ this.readonly = false;
11
+ this.max = 5;
12
+ }
13
+ render() {
14
+ const stars = Array.from({ length: this.max }, (_, i) => i + 1);
15
+ return (h("div", { key: '1e1c67012185e7aaecfcc89671532946262a6957', class: "wrap", role: this.readonly ? undefined : 'radiogroup' }, stars.map((s) => (h("span", { class: { star: true, filled: s <= this.value, clickable: !this.readonly }, onClick: () => !this.readonly && this.ratingChange.emit({ value: s }) }, "\u2605")))));
16
+ }
17
+ };
18
+ RecipeRating.style = recipeRatingCss();
19
+
20
+ export { RecipeRating as R };
21
+ //# sourceMappingURL=recipe-rating-BZDgwNdK.js.map
22
+
23
+ //# sourceMappingURL=recipe-rating-BZDgwNdK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-rating-BZDgwNdK.js","sources":["src/components/recipe-rating/recipe-rating.css?tag=recipe-rating&encapsulation=shadow","src/components/recipe-rating/recipe-rating.tsx"],"sourcesContent":[":host { display: inline-block; font-family: system-ui, sans-serif; }\n.wrap { display: flex; gap: 2px; }\n.star { font-size: 18px; color: #ddd; }\n.star.filled { color: #f5a623; }\n.star.clickable { cursor: pointer; }\n","import { Component, Prop, Event, EventEmitter, h } from '@stencil/core';\n\n/** 5-star rating. Read-only display or interactive input. */\n@Component({\n tag: 'recipe-rating',\n styleUrl: 'recipe-rating.css',\n shadow: true,\n})\nexport class RecipeRating {\n @Prop() value: number = 0;\n @Prop() readonly: boolean = false;\n @Prop() max: number = 5;\n\n @Event() ratingChange: EventEmitter<{ value: number }>;\n\n render() {\n const stars = Array.from({ length: this.max }, (_, i) => i + 1);\n return (\n <div class=\"wrap\" role={this.readonly ? undefined : 'radiogroup'}>\n {stars.map((s) => (\n <span\n class={{ star: true, filled: s <= this.value, clickable: !this.readonly }}\n onClick={() => !this.readonly && this.ratingChange.emit({ value: s })}\n >\n ★\n </span>\n ))}\n </div>\n );\n }\n}\n"],"names":[],"mappings":";;AAAA,MAAM,eAAe,GAAG,MAAM,CAAC,kLAAkL,CAAC;;MCQrM,YAAY,GAAA,MAAA;AALzB,IAAA,WAAA,CAAA,OAAA,EAAA;;;AAMU,QAAA,IAAA,CAAA,KAAK,GAAW,CAAC;AACjB,QAAA,IAAA,CAAA,QAAQ,GAAY,KAAK;AACzB,QAAA,IAAA,CAAA,GAAG,GAAW,CAAC;AAmBxB;IAfC,MAAM,GAAA;QACJ,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/D,QACE,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,MAAM,EAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,GAAG,SAAS,GAAG,YAAY,EAAA,EAC7D,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MACX,CAAA,CAAA,MAAA,EAAA,EACE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,EACzE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAA,EAAA,QAAA,CAGhE,CACR,CAAC,CACE;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ export { R as recipe_rating } from './recipe-rating-BZDgwNdK.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=recipe-rating.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=recipe-rating.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"recipe-rating.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,47 @@
1
+ import { r as registerInstance, a as createEvent, h } from './index-DV5P9Jd8.js';
2
+
3
+ const recipeSearchBarCss = () => `:host{display:block;font-family:system-ui, sans-serif}.wrap{display:flex;gap:6px}input{flex:1;padding:10px 14px;border-radius:8px;border:1px solid #ddd;font-size:14px;outline:none}input:focus{border-color:#ff6b35}button{border:none;background:#ff6b35;color:#fff;border-radius:8px;padding:0 16px;cursor:pointer;font-size:15px}`;
4
+
5
+ const RecipeSearchBar = class {
6
+ constructor(hostRef) {
7
+ registerInstance(this, hostRef);
8
+ this.search = createEvent(this, "search", 7);
9
+ this.placeholder = 'Search recipes...';
10
+ this.value = '';
11
+ this.debounceMs = 350;
12
+ this.internalValue = '';
13
+ this.onInput = (e) => {
14
+ this.internalValue = e.target.value;
15
+ clearTimeout(this.timer);
16
+ this.timer = setTimeout(() => {
17
+ this.search.emit({ value: this.internalValue });
18
+ }, this.debounceMs);
19
+ };
20
+ this.onKeyDown = (e) => {
21
+ if (e.key === 'Enter') {
22
+ clearTimeout(this.timer);
23
+ this.search.emit({ value: this.internalValue });
24
+ }
25
+ };
26
+ }
27
+ componentWillLoad() {
28
+ this.internalValue = this.value;
29
+ }
30
+ onValuePropChange(newVal) {
31
+ this.internalValue = newVal;
32
+ }
33
+ render() {
34
+ return (h("div", { key: 'fe5eb2928308fb2496badd784e684b19df52501a', class: "wrap" }, h("input", { key: '3d4abb23a1155075a8889e015819b37a8569f6f5', type: "search", placeholder: this.placeholder, value: this.internalValue, onInput: this.onInput, onKeyDown: this.onKeyDown }), h("button", { key: 'f33f95b5bfcad9146903a2eb4448932a1d68edb7', onClick: () => this.search.emit({ value: this.internalValue }), "aria-label": "Search" }, "\uD83D\uDD0D")));
35
+ }
36
+ static get watchers() { return {
37
+ "value": [{
38
+ "onValuePropChange": 0
39
+ }]
40
+ }; }
41
+ };
42
+ RecipeSearchBar.style = recipeSearchBarCss();
43
+
44
+ export { RecipeSearchBar as R };
45
+ //# sourceMappingURL=recipe-search-bar-BVbznAHF.js.map
46
+
47
+ //# sourceMappingURL=recipe-search-bar-BVbznAHF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-search-bar-BVbznAHF.js","sources":["src/components/recipe-search-bar/recipe-search-bar.css?tag=recipe-search-bar&encapsulation=shadow","src/components/recipe-search-bar/recipe-search-bar.tsx"],"sourcesContent":[":host { display: block; font-family: system-ui, sans-serif; }\n.wrap { display: flex; gap: 6px; }\ninput {\n flex: 1; padding: 10px 14px; border-radius: 8px;\n border: 1px solid #ddd; font-size: 14px; outline: none;\n}\ninput:focus { border-color: #ff6b35; }\nbutton {\n border: none; background: #ff6b35; color: #fff; border-radius: 8px;\n padding: 0 16px; cursor: pointer; font-size: 15px;\n}\n","import { Component, Prop, State, Event, EventEmitter, h, Watch } from '@stencil/core';\n\n/**\n * A debounced search input. Emits `search` with the current value\n * after the user stops typing (default 350ms) or presses Enter.\n */\n@Component({\n tag: 'recipe-search-bar',\n styleUrl: 'recipe-search-bar.css',\n shadow: true,\n})\nexport class RecipeSearchBar {\n @Prop() placeholder: string = 'Search recipes...';\n @Prop() value: string = '';\n @Prop() debounceMs: number = 350;\n\n @State() internalValue: string = '';\n\n @Event() search: EventEmitter<{ value: string }>;\n\n private timer: any;\n\n componentWillLoad() {\n this.internalValue = this.value;\n }\n\n @Watch('value')\n onValuePropChange(newVal: string) {\n this.internalValue = newVal;\n }\n\n private onInput = (e: InputEvent) => {\n this.internalValue = (e.target as HTMLInputElement).value;\n clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.search.emit({ value: this.internalValue });\n }, this.debounceMs);\n };\n\n private onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n clearTimeout(this.timer);\n this.search.emit({ value: this.internalValue });\n }\n };\n\n render() {\n return (\n <div class=\"wrap\">\n <input\n type=\"search\"\n placeholder={this.placeholder}\n value={this.internalValue}\n onInput={this.onInput}\n onKeyDown={this.onKeyDown}\n />\n <button onClick={() => this.search.emit({ value: this.internalValue })} aria-label=\"Search\">\n 🔍\n </button>\n </div>\n );\n }\n}\n"],"names":[],"mappings":";;AAAA,MAAM,kBAAkB,GAAG,MAAM,CAAC,qUAAqU,CAAC;;MCW3V,eAAe,GAAA,MAAA;AAL5B,IAAA,WAAA,CAAA,OAAA,EAAA;;;AAMU,QAAA,IAAA,CAAA,WAAW,GAAW,mBAAmB;AACzC,QAAA,IAAA,CAAA,KAAK,GAAW,EAAE;AAClB,QAAA,IAAA,CAAA,UAAU,GAAW,GAAG;AAEvB,QAAA,IAAA,CAAA,aAAa,GAAW,EAAE;AAe3B,QAAA,IAAA,CAAA,OAAO,GAAG,CAAC,CAAa,KAAI;YAClC,IAAI,CAAC,aAAa,GAAI,CAAC,CAAC,MAA2B,CAAC,KAAK;AACzD,YAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;AACxB,YAAA,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,MAAK;AAC3B,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;AACjD,aAAC,EAAE,IAAI,CAAC,UAAU,CAAC;AACrB,SAAC;AAEO,QAAA,IAAA,CAAA,SAAS,GAAG,CAAC,CAAgB,KAAI;AACvC,YAAA,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE;AACrB,gBAAA,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;AACxB,gBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC;;AAEnD,SAAC;AAkBF;IAxCC,iBAAiB,GAAA;AACf,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK;;AAIjC,IAAA,iBAAiB,CAAC,MAAc,EAAA;AAC9B,QAAA,IAAI,CAAC,aAAa,GAAG,MAAM;;IAkB7B,MAAM,GAAA;QACJ,QACE,CAAA,CAAA,KAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAK,KAAK,EAAC,MAAM,EAAA,EACf,CAAA,CAAA,OAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EACE,IAAI,EAAC,QAAQ,EACb,WAAW,EAAE,IAAI,CAAC,WAAW,EAC7B,KAAK,EAAE,IAAI,CAAC,aAAa,EACzB,OAAO,EAAE,IAAI,CAAC,OAAO,EACrB,SAAS,EAAE,IAAI,CAAC,SAAS,EAAA,CACzB,EACF,CAAA,CAAA,QAAA,EAAA,EAAA,GAAA,EAAA,0CAAA,EAAQ,OAAO,EAAE,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,gBAAa,QAAQ,EAAA,EAAA,cAAA,CAElF,CACL;;;;;;;;;;;;"}
@@ -0,0 +1,5 @@
1
+ export { R as recipe_search_bar } from './recipe-search-bar-BVbznAHF.js';
2
+ import './index-DV5P9Jd8.js';
3
+ //# sourceMappingURL=recipe-search-bar.entry.esm.js.map
4
+
5
+ //# sourceMappingURL=recipe-search-bar.entry.js.map
@@ -0,0 +1 @@
1
+ {"file":"recipe-search-bar.entry.js","mappings":";","names":[],"sources":[],"sourcesContent":[],"version":3}
@@ -0,0 +1,50 @@
1
+ import { B as BUILD, c as consoleDevInfo, H, w as win, N as NAMESPACE, p as promiseResolve, g as globalScripts, b as bootstrapLazy } from './index-DV5P9Jd8.js';
2
+ export { s as setNonce } from './index-DV5P9Jd8.js';
3
+
4
+ /*
5
+ Stencil Client Patch Browser v4.44.0 | MIT Licensed | https://stenciljs.com
6
+ */
7
+
8
+ var patchBrowser = () => {
9
+ if (BUILD.isDev && !BUILD.isTesting) {
10
+ consoleDevInfo("Running in development mode.");
11
+ }
12
+ if (BUILD.cloneNodeFix) {
13
+ patchCloneNodeFix(H.prototype);
14
+ }
15
+ const scriptElm = BUILD.scriptDataOpts ? win.document && Array.from(win.document.querySelectorAll("script")).find(
16
+ (s) => new RegExp(`/${NAMESPACE}(\\.esm)?\\.js($|\\?|#)`).test(s.src) || s.getAttribute("data-stencil-namespace") === NAMESPACE
17
+ ) : null;
18
+ const importMeta = import.meta.url;
19
+ const opts = BUILD.scriptDataOpts ? (scriptElm || {})["data-opts"] || {} : {};
20
+ if (importMeta !== "") {
21
+ opts.resourcesUrl = new URL(".", importMeta).href;
22
+ }
23
+ return promiseResolve(opts);
24
+ };
25
+ var patchCloneNodeFix = (HTMLElementPrototype) => {
26
+ const nativeCloneNodeFn = HTMLElementPrototype.cloneNode;
27
+ HTMLElementPrototype.cloneNode = function(deep) {
28
+ if (this.nodeName === "TEMPLATE") {
29
+ return nativeCloneNodeFn.call(this, deep);
30
+ }
31
+ const clonedNode = nativeCloneNodeFn.call(this, false);
32
+ const srcChildNodes = this.childNodes;
33
+ if (deep) {
34
+ for (let i = 0; i < srcChildNodes.length; i++) {
35
+ if (srcChildNodes[i].nodeType !== 2) {
36
+ clonedNode.appendChild(srcChildNodes[i].cloneNode(true));
37
+ }
38
+ }
39
+ }
40
+ return clonedNode;
41
+ };
42
+ };
43
+
44
+ patchBrowser().then(async (options) => {
45
+ await globalScripts();
46
+ return bootstrapLazy([["app-modal",[[257,"app-modal",{"open":[4],"modalTitle":[1,"modal-title"]}]]],["app-toast",[[1,"app-toast",{"message":[1],"type":[1],"duration":[2]}]]],["meal-plan-day",[[1,"meal-plan-day",{"day":[1],"meals":[16]}]]],["recipe-card",[[257,"recipe-card",{"recipeTitle":[1,"recipe-title"],"image":[1],"category":[1],"favorite":[4]}]]],["recipe-filter-bar",[[1,"recipe-filter-bar",{"categories":[16],"active":[1]}]]],["recipe-form",[[257,"recipe-form",{"initialData":[1,"initial-data"],"title":[32],"image":[32],"category":[32],"ingredients":[32],"instructions":[32],"errors":[32]},null,{"initialData":[{"onInitialDataChange":0}]}]]],["recipe-rating",[[1,"recipe-rating",{"value":[2],"readonly":[4],"max":[2]}]]],["recipe-search-bar",[[1,"recipe-search-bar",{"placeholder":[1],"value":[1],"debounceMs":[2,"debounce-ms"],"internalValue":[32]},null,{"value":[{"onValuePropChange":0}]}]]]], options);
47
+ });
48
+ //# sourceMappingURL=recipe-ui.esm.js.map
49
+
50
+ //# sourceMappingURL=recipe-ui.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recipe-ui.esm.js","sources":["node_modules/@stencil/core/internal/client/patch-browser.js","@lazy-browser-entrypoint?app-data=conditional"],"sourcesContent":["/*\n Stencil Client Patch Browser v4.44.0 | MIT Licensed | https://stenciljs.com\n */\n\n// src/client/client-patch-browser.ts\nimport { BUILD, NAMESPACE } from \"@stencil/core/internal/app-data\";\nimport { consoleDevInfo, H, promiseResolve, win } from \"@stencil/core\";\nvar patchBrowser = () => {\n if (BUILD.isDev && !BUILD.isTesting) {\n consoleDevInfo(\"Running in development mode.\");\n }\n if (BUILD.cloneNodeFix) {\n patchCloneNodeFix(H.prototype);\n }\n const scriptElm = BUILD.scriptDataOpts ? win.document && Array.from(win.document.querySelectorAll(\"script\")).find(\n (s) => new RegExp(`/${NAMESPACE}(\\\\.esm)?\\\\.js($|\\\\?|#)`).test(s.src) || s.getAttribute(\"data-stencil-namespace\") === NAMESPACE\n ) : null;\n const importMeta = import.meta.url;\n const opts = BUILD.scriptDataOpts ? (scriptElm || {})[\"data-opts\"] || {} : {};\n if (importMeta !== \"\") {\n opts.resourcesUrl = new URL(\".\", importMeta).href;\n }\n return promiseResolve(opts);\n};\nvar patchCloneNodeFix = (HTMLElementPrototype) => {\n const nativeCloneNodeFn = HTMLElementPrototype.cloneNode;\n HTMLElementPrototype.cloneNode = function(deep) {\n if (this.nodeName === \"TEMPLATE\") {\n return nativeCloneNodeFn.call(this, deep);\n }\n const clonedNode = nativeCloneNodeFn.call(this, false);\n const srcChildNodes = this.childNodes;\n if (deep) {\n for (let i = 0; i < srcChildNodes.length; i++) {\n if (srcChildNodes[i].nodeType !== 2) {\n clonedNode.appendChild(srcChildNodes[i].cloneNode(true));\n }\n }\n }\n return clonedNode;\n };\n};\nexport {\n patchBrowser\n};\n","export { setNonce } from '@stencil/core';\nimport { bootstrapLazy } from '@stencil/core';\nimport { patchBrowser } from '@stencil/core/internal/client/patch-browser';\nimport { globalScripts } from '@stencil/core/internal/app-globals';\npatchBrowser().then(async (options) => {\n await globalScripts();\n return bootstrapLazy([/*!__STENCIL_LAZY_DATA__*/], options);\n});\n"],"names":[],"mappings":";;;AAAA;AACA;AACA;;AAKA,IAAI,YAAY,GAAG,MAAM;AACzB,EAAE,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE;AACvC,IAAI,cAAc,CAAC,8BAA8B,CAAC;AAClD;AACA,EAAE,IAAI,KAAK,CAAC,YAAY,EAAE;AAC1B,IAAI,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;AAClC;AACA,EAAE,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,GAAG,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI;AACnH,IAAI,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,wBAAwB,CAAC,KAAK;AAC1H,GAAG,GAAG,IAAI;AACV,EAAE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG;AACpC,EAAE,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE;AAC/E,EAAE,IAAI,UAAU,KAAK,EAAE,EAAE;AACzB,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,IAAI;AACrD;AACA,EAAE,OAAO,cAAc,CAAC,IAAI,CAAC;AAC7B,CAAC;AACD,IAAI,iBAAiB,GAAG,CAAC,oBAAoB,KAAK;AAClD,EAAE,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,SAAS;AAC1D,EAAE,oBAAoB,CAAC,SAAS,GAAG,SAAS,IAAI,EAAE;AAClD,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,UAAU,EAAE;AACtC,MAAM,OAAO,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC;AAC/C;AACA,IAAI,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC;AAC1D,IAAI,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU;AACzC,IAAI,IAAI,IAAI,EAAE;AACd,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrD,QAAQ,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,EAAE;AAC7C,UAAU,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;AAClE;AACA;AACA;AACA,IAAI,OAAO,UAAU;AACrB,GAAG;AACH,CAAC;;ACrCD,YAAY,EAAE,CAAC,IAAI,CAAC,OAAO,OAAO,KAAK;AACvC,EAAE,MAAM,aAAa,EAAE;AACvB,EAAE,OAAO,aAAa,CAAC,4BAA4B,EAAE,OAAO,CAAC;AAC7D,CAAC,CAAC","x_google_ignoreList":[0]}
@@ -0,0 +1,8 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ /** Generic modal dialog. Body content is provided via the default slot. */
3
+ export declare class AppModal {
4
+ open: boolean;
5
+ modalTitle: string;
6
+ close: EventEmitter<void>;
7
+ render(): any;
8
+ }
@@ -0,0 +1,10 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ /** Simple notification toast. Auto-dismisses after `duration` ms if > 0. */
3
+ export declare class AppToast {
4
+ message: string;
5
+ type: 'success' | 'error' | 'info';
6
+ duration: number;
7
+ dismiss: EventEmitter<void>;
8
+ componentDidLoad(): void;
9
+ render(): any;
10
+ }
@@ -0,0 +1,25 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ export interface PlannedMeal {
3
+ id: string;
4
+ title: string;
5
+ image?: string;
6
+ }
7
+ /**
8
+ * One column of the weekly meal planner, representing a single day.
9
+ * `meals` is the list of recipes currently assigned to this day.
10
+ * Emits `removeMeal` when a meal's remove button is clicked, and
11
+ * `assignMeal` when the "+ Add meal" button is clicked (the parent app
12
+ * is expected to open its own picker/modal and then update `meals`).
13
+ */
14
+ export declare class MealPlanDay {
15
+ day: string;
16
+ meals: PlannedMeal[];
17
+ assignMeal: EventEmitter<{
18
+ day: string;
19
+ }>;
20
+ removeMeal: EventEmitter<{
21
+ day: string;
22
+ mealId: string;
23
+ }>;
24
+ render(): any;
25
+ }
@@ -0,0 +1,26 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ /**
3
+ * Displays a summary of a recipe (image, title, category) inside a card.
4
+ * Emits `cardClick` when the card body is clicked and `favoriteToggle`
5
+ * when the favorite button is pressed. Accepts a named `actions` slot
6
+ * for extra buttons supplied by the consuming app.
7
+ */
8
+ export declare class RecipeCard {
9
+ /** Recipe title (named recipeTitle, not title, to avoid colliding with
10
+ * the native HTML title/tooltip attribute every element already has) */
11
+ recipeTitle: string;
12
+ /** Image URL */
13
+ image: string;
14
+ /** Category / cuisine label */
15
+ category: string;
16
+ /** Whether this recipe is currently favorited */
17
+ favorite: boolean;
18
+ /** Fired when the favorite button is toggled */
19
+ favoriteToggle: EventEmitter<{
20
+ favorite: boolean;
21
+ }>;
22
+ /** Fired when the card (excluding the favorite button) is clicked */
23
+ cardClick: EventEmitter<void>;
24
+ private onFavoriteClick;
25
+ render(): any;
26
+ }
@@ -0,0 +1,16 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ /**
3
+ * Renders a row of selectable filter chips (e.g. categories/cuisines).
4
+ * `categories` accepts either a JSON string array or an actual array
5
+ * (Stencil auto-parses complex props passed as attributes).
6
+ * Emits `filterChange` with the selected value ('' = all).
7
+ */
8
+ export declare class RecipeFilterBar {
9
+ categories: string[];
10
+ active: string;
11
+ filterChange: EventEmitter<{
12
+ value: string;
13
+ }>;
14
+ private select;
15
+ render(): any;
16
+ }
@@ -0,0 +1,35 @@
1
+ import { EventEmitter } from '../../stencil-public-runtime';
2
+ export interface RecipeFormData {
3
+ id?: string;
4
+ title: string;
5
+ image: string;
6
+ category: string;
7
+ ingredients: string[];
8
+ instructions: string;
9
+ }
10
+ /**
11
+ * Add/edit form for user-created recipes. Performs client-side validation
12
+ * (title required, at least one non-empty ingredient, instructions required)
13
+ * and only emits `save` once the data is valid.
14
+ */
15
+ export declare class RecipeForm {
16
+ /** Pass an existing recipe (as a JSON string or object) to edit it */
17
+ initialData: RecipeFormData | string;
18
+ title: string;
19
+ image: string;
20
+ category: string;
21
+ ingredients: string[];
22
+ instructions: string;
23
+ errors: Record<string, string>;
24
+ save: EventEmitter<RecipeFormData>;
25
+ cancel: EventEmitter<void>;
26
+ componentWillLoad(): void;
27
+ onInitialDataChange(): void;
28
+ private loadInitialData;
29
+ private validate;
30
+ private onSubmit;
31
+ private updateIngredient;
32
+ private addIngredient;
33
+ private removeIngredient;
34
+ render(): any;
35
+ }