@gurpinderjitsingh/recipe-ui 0.12.0 → 0.13.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 (33) hide show
  1. package/dist/cjs/app-modal_7.cjs.entry.js +150 -47
  2. package/dist/cjs/app-toast.cjs.entry.js +1 -1
  3. package/dist/collection/components/app-toast/app-toast.js +1 -1
  4. package/dist/collection/components/recipe-card/recipe-card.js +1 -1
  5. package/dist/collection/components/recipe-filter-bar/recipe-filter-bar.js +1 -1
  6. package/dist/collection/components/recipe-form/recipe-form.css +4 -0
  7. package/dist/collection/components/recipe-form/recipe-form.js +144 -41
  8. package/dist/collection/components/recipe-rating/recipe-rating.js +1 -1
  9. package/dist/collection/components/recipe-search-bar/recipe-search-bar.js +2 -2
  10. package/dist/components/app-toast.js +1 -1
  11. package/dist/components/index.js +1 -1
  12. package/dist/components/p-2Oa3Ks4e.js +1 -0
  13. package/dist/components/{p-DElrp5qn.js → p-BWjrK8cx.js} +1 -1
  14. package/dist/components/{p-BBMv9wjr.js → p-BjJGcNvA.js} +1 -1
  15. package/dist/components/p-DPXKwfGM.js +1 -0
  16. package/dist/components/{p-B_OSW9IJ.js → p-HEDJ6pyO.js} +1 -1
  17. package/dist/components/{p-CayP90fv.js → p-ltkIKdRy.js} +1 -1
  18. package/dist/components/recipe-card.js +1 -1
  19. package/dist/components/recipe-filter-bar.js +1 -1
  20. package/dist/components/recipe-form.js +1 -1
  21. package/dist/components/recipe-rating.js +1 -1
  22. package/dist/components/recipe-search-bar.js +1 -1
  23. package/dist/esm/app-modal_7.entry.js +150 -47
  24. package/dist/esm/app-toast.entry.js +1 -1
  25. package/dist/recipe-ui/p-7e9d3224.entry.js +1 -0
  26. package/dist/recipe-ui/p-dca78e59.entry.js +1 -0
  27. package/dist/recipe-ui/recipe-ui.esm.js +1 -1
  28. package/dist/types/components/recipe-form/recipe-form.d.ts +15 -3
  29. package/package.json +1 -1
  30. package/dist/components/p-8ogxleRP.js +0 -1
  31. package/dist/components/p-CcOtmzb-.js +0 -1
  32. package/dist/recipe-ui/p-13f32fd3.entry.js +0 -1
  33. package/dist/recipe-ui/p-9c0be643.entry.js +0 -1
@@ -11,6 +11,14 @@ const DEFAULT_AREAS = [
11
11
  'Portuguese', 'Russian', 'Spanish', 'Thai', 'Tunisian', 'Turkish',
12
12
  'Vietnamese', 'Other',
13
13
  ];
14
+ const LIMITS = {
15
+ title: { min: 5, max: 100 },
16
+ instructions: { min: 20, max: 5000 },
17
+ ingredient: { min: 5, max: 200 },
18
+ customCategory: { min: 5, max: 40 },
19
+ customArea: { min: 5, max: 40 },
20
+ image: { max: 500 },
21
+ };
14
22
  export class RecipeForm {
15
23
  constructor() {
16
24
  this.categories = DEFAULT_CATEGORIES;
@@ -47,10 +55,9 @@ export class RecipeForm {
47
55
  this.ingredients = [...this.ingredients, ''];
48
56
  };
49
57
  this.removeIngredient = (index) => {
50
- this.ingredients = this.ingredients.filter((_, i) => i !== index);
51
58
  const next = this.ingredients.filter((_, i) => i !== index);
52
59
  this.ingredients = next;
53
- this.clearErrorIf('ingredients', next.some((i) => i.trim()));
60
+ this.setFieldError('ingredients', this.validateIngredients(next));
54
61
  };
55
62
  }
56
63
  componentWillLoad() {
@@ -75,10 +82,83 @@ export class RecipeForm {
75
82
  get fullAreaList() {
76
83
  return this.mergeList(this.areas, this.extraAreas);
77
84
  }
78
- /** Removes a field's error as soon as it becomes valid, without waiting
79
- * for the next full submit — called from each field's input handler. */
80
- clearErrorIf(key, isNowValid) {
81
- if (isNowValid && this.errors[key]) {
85
+ // ---- Per-field validators. Each returns an error message, or null when
86
+ // the value is valid. Used both by validate() on submit AND live on every
87
+ // keystroke, so the two can never drift out of sync with each other. ----
88
+ validateTitle(value) {
89
+ const len = value.trim().length;
90
+ if (len === 0)
91
+ return 'Title is required.';
92
+ if (len < LIMITS.title.min)
93
+ return `Title must be at least ${LIMITS.title.min} characters.`;
94
+ if (len > LIMITS.title.max)
95
+ return `Title must be ${LIMITS.title.max} characters or fewer.`;
96
+ return null;
97
+ }
98
+ validateImage(value) {
99
+ if (value.trim().length > LIMITS.image.max) {
100
+ return `Image URL must be ${LIMITS.image.max} characters or fewer.`;
101
+ }
102
+ return null;
103
+ }
104
+ validateIngredients(list) {
105
+ const clean = list.map((i) => i.trim()).filter(Boolean);
106
+ if (clean.length === 0)
107
+ return 'Add at least one ingredient.';
108
+ if (clean.some((i) => i.length < LIMITS.ingredient.min)) {
109
+ return `Each ingredient must be at least ${LIMITS.ingredient.min} characters.`;
110
+ }
111
+ if (clean.some((i) => i.length > LIMITS.ingredient.max)) {
112
+ return `Each ingredient must be ${LIMITS.ingredient.max} characters or fewer.`;
113
+ }
114
+ return null;
115
+ }
116
+ validateInstructions(value) {
117
+ const len = value.trim().length;
118
+ if (len === 0)
119
+ return 'Instructions are required.';
120
+ if (len < LIMITS.instructions.min) {
121
+ return `Instructions must be at least ${LIMITS.instructions.min} characters — add a bit more detail.`;
122
+ }
123
+ if (len > LIMITS.instructions.max)
124
+ return `Instructions must be ${LIMITS.instructions.max} characters or fewer.`;
125
+ return null;
126
+ }
127
+ validateCategory(value) {
128
+ return value.trim() ? null : 'Category is required.';
129
+ }
130
+ validateCustomCategory(value) {
131
+ const len = value.trim().length;
132
+ if (len === 0)
133
+ return 'Enter a category name.';
134
+ if (len < LIMITS.customCategory.min || len > LIMITS.customCategory.max) {
135
+ return `Category name must be ${LIMITS.customCategory.min}-${LIMITS.customCategory.max} characters.`;
136
+ }
137
+ return null;
138
+ }
139
+ validateArea(value) {
140
+ return value.trim() ? null : 'Cuisine / Area is required.';
141
+ }
142
+ validateCustomArea(value) {
143
+ const len = value.trim().length;
144
+ if (len === 0)
145
+ return 'Enter a cuisine name.';
146
+ if (len < LIMITS.customArea.min || len > LIMITS.customArea.max) {
147
+ return `Cuisine name must be ${LIMITS.customArea.min}-${LIMITS.customArea.max} characters.`;
148
+ }
149
+ return null;
150
+ }
151
+ /** Sets or clears a single field's error live — the core of "show error
152
+ * if any, else clear" behavior. Only touches `errors` when the message
153
+ * actually changed, to avoid redundant re-renders on every keystroke. */
154
+ setFieldError(key, message) {
155
+ const current = this.errors[key] ?? null;
156
+ if (current === message)
157
+ return;
158
+ if (message) {
159
+ this.errors = { ...this.errors, [key]: message };
160
+ }
161
+ else {
82
162
  const next = { ...this.errors };
83
163
  delete next[key];
84
164
  this.errors = next;
@@ -120,28 +200,45 @@ export class RecipeForm {
120
200
  this.area = data.cuisine ?? '';
121
201
  this.customArea = '';
122
202
  }
203
+ // Reset any stale errors when the form is (re)populated, e.g. when
204
+ // switching from "new recipe" to editing an existing one.
205
+ this.errors = {};
123
206
  }
124
207
  }
208
+ /** Full validation pass, run on submit. Reuses the same per-field
209
+ * validators as live typing, so submit can never disagree with what
210
+ * the user already saw on screen. */
125
211
  validate() {
126
212
  const errors = {};
127
- if (!this.title.trim())
128
- errors.title = 'Title is required.';
129
- const cleanIngredients = this.ingredients.map((i) => i.trim()).filter(Boolean);
130
- if (cleanIngredients.length === 0)
131
- errors.ingredients = 'Add at least one ingredient.';
132
- if (!this.instructions.trim())
133
- errors.instructions = 'Instructions are required.';
134
- if (!this.category.trim()) {
135
- errors.category = 'Category is required.';
213
+ const titleErr = this.validateTitle(this.title);
214
+ if (titleErr)
215
+ errors.title = titleErr;
216
+ const imageErr = this.validateImage(this.image);
217
+ if (imageErr)
218
+ errors.image = imageErr;
219
+ const ingredientsErr = this.validateIngredients(this.ingredients);
220
+ if (ingredientsErr)
221
+ errors.ingredients = ingredientsErr;
222
+ const instructionsErr = this.validateInstructions(this.instructions);
223
+ if (instructionsErr)
224
+ errors.instructions = instructionsErr;
225
+ const categoryErr = this.validateCategory(this.category);
226
+ if (categoryErr) {
227
+ errors.category = categoryErr;
136
228
  }
137
- else if (this.category === 'Other' && !this.customCategory.trim()) {
138
- errors.customCategory = 'Enter a category name.';
229
+ else if (this.category === 'Other') {
230
+ const customCategoryErr = this.validateCustomCategory(this.customCategory);
231
+ if (customCategoryErr)
232
+ errors.customCategory = customCategoryErr;
139
233
  }
140
- if (!this.area.trim()) {
141
- errors.area = 'Cuisine / Area is required.';
234
+ const areaErr = this.validateArea(this.area);
235
+ if (areaErr) {
236
+ errors.area = areaErr;
142
237
  }
143
- else if (this.area === 'Other' && !this.customArea.trim()) {
144
- errors.customArea = 'Enter a cuisine name.';
238
+ else if (this.area === 'Other') {
239
+ const customAreaErr = this.validateCustomArea(this.customArea);
240
+ if (customAreaErr)
241
+ errors.customArea = customAreaErr;
145
242
  }
146
243
  this.errors = errors;
147
244
  return Object.keys(errors).length === 0;
@@ -150,33 +247,39 @@ export class RecipeForm {
150
247
  const next = [...this.ingredients];
151
248
  next[index] = value;
152
249
  this.ingredients = next;
153
- this.clearErrorIf('ingredients', next.some((i) => i.trim()));
250
+ this.setFieldError('ingredients', this.validateIngredients(next));
154
251
  }
155
252
  render() {
156
- return (h("form", { key: 'c29ab62af9db4a95e11b75f25a2dd4c23e699dad', onSubmit: this.onSubmit }, h("slot", { key: 'ece02ab46a1075eaef912e8953e70faa11bc8d8d', name: "header" }), h("label", { key: '9e17e9ba311350d9235f5ec01c32743dfbf55b0e' }, "Title", h("input", { key: '2b8d632bc22eb5cde52198287ea2b19d87567dba', type: "text", value: this.title, onInput: (e) => {
157
- (this.title = e.target.value);
158
- this.clearErrorIf('title', !!this.title.trim());
159
- } }), this.errors.title && h("span", { key: '3a34d1f5e1f43ef190983dd0883f16eb6775f07c', class: "error" }, this.errors.title)), h("label", { key: '6acda6ccaff2e0315cc1964ae30c18647fe97bd1' }, "Image URL", h("input", { key: '6bdc06a2bebd6b5ae984865aefe292426fbe38cb', type: "text", value: this.image, onInput: (e) => (this.image = e.target.value) })), h("div", { key: 'f6ef49aaace211659296e93b4b5b47b29db73ac6', class: "row" }, h("label", { key: '9610c81a85ebddc2da4bdadcdee68ddea78e28d4' }, "Category", h("select", { key: '46542ff9e2c0f15d60be6744e063f3c860475772', onInput: (e) => {
160
- (this.category = e.target.value);
161
- this.clearErrorIf('category', !!this.category);
253
+ return (h("form", { key: 'f562e5fa4cdd1f49b6b119471bf7773e319423e8', onSubmit: this.onSubmit }, h("slot", { key: '0bcfd3c288244952e69b1206449d6046ff9f832b', name: "header" }), h("label", { key: 'b94ca99a985de9ab0a4a827854c6e9f3c2c32228' }, h("span", { key: 'ea3f3a53edb24ec2a8421c2b97fee17877fa507a' }, "Title ", h("span", { key: 'ef3093980bc013d65787ae4a0e04c5a86564433e', class: "required" }, "*")), h("input", { key: 'f9748efc0027f3783d2961d11524c59ca58121fe', type: "text", maxLength: LIMITS.title.max, value: this.title, onInput: (e) => {
254
+ this.title = e.target.value;
255
+ this.setFieldError('title', this.validateTitle(this.title));
256
+ } }), h("span", { key: '0ac83523c019253560b219137390b8fdd3626323', class: "char-count" }, this.title.trim().length, "/", LIMITS.title.max), this.errors.title && h("span", { key: '2f1a55d2aa593182c3fe6330b3248985331aca2e', class: "error" }, this.errors.title)), h("label", { key: 'aa7d01fd11b9b4ea0551e8a157a70a666b645720' }, "Image URL", h("input", { key: 'fd26aabc19167c469419a1714358615f62b125b9', type: "text", maxLength: LIMITS.image.max, value: this.image, onInput: (e) => {
257
+ this.image = e.target.value;
258
+ this.setFieldError('image', this.validateImage(this.image));
259
+ } }), this.errors.image && h("span", { key: '3f9561c7ee8433cee875c638edcb069baef96b86', class: "error" }, this.errors.image)), h("div", { key: '4253c71219c77f40ef8ec811700fa2ffb4e23d55', class: "row" }, h("label", { key: '7b892f3e2e87d7ab647050859996f7fcccbec42c' }, h("span", { key: '3f63f3f57f9df8673b54c42251282918fee1be69' }, "Category ", h("span", { key: 'd535dbd85bc96e1af88511f903d96046f23cb283', class: "required" }, "*")), h("select", { key: '311099318b5b7e6212eae0c9803adcf2437aa024', onInput: (e) => {
260
+ this.category = e.target.value;
261
+ this.setFieldError('category', this.validateCategory(this.category));
162
262
  if (this.category !== 'Other')
163
- this.clearErrorIf('customCategory', true);
164
- } }, h("option", { key: '2741b90edd32cb12903e877cbfa2db3b5ff04071', value: "", selected: this.category === '' }, "Select a category\u2026"), this.fullCategoryList.map((c) => (h("option", { value: c, selected: c === this.category }, c)))), this.errors.category && h("span", { key: 'dc8e7ab5a6decdece1b4559af856997251335abf', class: "error" }, this.errors.category)), h("label", { key: '792cb1c4fd6052f9d88c52649c6eb4dc064f0071' }, "Cuisine / Area", h("select", { key: '06a917ac951a4c6d3e8390534690545a1967581a', onInput: (e) => {
165
- (this.area = e.target.value);
166
- this.clearErrorIf('area', !!this.area);
263
+ this.setFieldError('customCategory', null);
264
+ else
265
+ this.setFieldError('customCategory', this.validateCustomCategory(this.customCategory));
266
+ } }, h("option", { key: '9ab9ed037d1cabc6829cf35bf8c77d5ae8ff6518', value: "", selected: this.category === '' }, "Select a category\u2026"), this.fullCategoryList.map((c) => (h("option", { value: c, selected: c === this.category }, c)))), this.errors.category && h("span", { key: '48a10209ff1b6425220388ba0f315899947bb37a', class: "error" }, this.errors.category)), h("label", { key: '294923e0daebc5aa126e66c6e8319ca087544dc4' }, h("span", { key: 'e9fc761f634c20cfc02829fcfeb4767dfaf4c69f' }, "Cuisine / Area ", h("span", { key: '52254653a745ef5459e7efd55d688e37324d358e', class: "required" }, "*")), h("select", { key: '7938a0a5b0bb6004b29578aee87cf5711e1d935d', onInput: (e) => {
267
+ this.area = e.target.value;
268
+ this.setFieldError('area', this.validateArea(this.area));
167
269
  if (this.area !== 'Other')
168
- this.clearErrorIf('customArea', true);
169
- } }, h("option", { key: '264c39814065b3f07626975b96eca6b7e1c9bb83', value: "", selected: this.area === '' }, "Select a cuisine\u2026"), this.fullAreaList.map((a) => (h("option", { value: a, selected: a === this.area }, a)))), this.errors.area && h("span", { key: 'f1a562402420fb7a813dd4184b439c03c71d93d9', class: "error" }, this.errors.area))), (this.category === 'Other' || this.area === 'Other') && (h("div", { key: '3c20e6b5b678027d711c7976c60e3be92d02fae3', class: "row" }, this.category === 'Other' && (h("label", { key: '4cd14baeff186c7db8fabe57b21b241567528437', class: "custom-area-field" }, "Enter category name", h("input", { key: 'b1880e7aaadb3ade3c980c7f44d6b0a8d4fc3923', type: "text", placeholder: "e.g. Brunch", value: this.customCategory, onInput: (e) => {
170
- (this.customCategory = e.target.value);
270
+ this.setFieldError('customArea', null);
271
+ else
272
+ this.setFieldError('customArea', this.validateCustomArea(this.customArea));
273
+ } }, h("option", { key: '2e3a43ac518d6d6662cda3c269055cac2d142cd0', value: "", selected: this.area === '' }, "Select a cuisine\u2026"), this.fullAreaList.map((a) => (h("option", { value: a, selected: a === this.area }, a)))), this.errors.area && h("span", { key: '9776cc4829e490a06c34ba6e6d8482fbda1499af', class: "error" }, this.errors.area))), (this.category === 'Other' || this.area === 'Other') && (h("div", { key: 'edf7709c6bb1fdf2d11d360dd4bff73067ace685', class: "row" }, this.category === 'Other' && (h("label", { key: 'e1b54a1298d4d9b7f2db16be20095c229fd7d72b', class: "custom-area-field" }, "Enter category name", h("input", { key: 'e538a1f8da6e55875d7b37538a205bfe09c7e48a', type: "text", placeholder: "e.g. Brunch", maxLength: LIMITS.customCategory.max, value: this.customCategory, onInput: (e) => {
171
274
  this.customCategory = e.target.value;
172
- this.clearErrorIf('customCategory', !!this.customCategory.trim());
173
- } }), this.errors.customCategory && h("span", { key: 'abd5094a23a182458ff2b76a58b55d65a9540c83', class: "error" }, this.errors.customCategory))), this.area === 'Other' && (h("label", { key: '696c0cc6af2cf16df8559da441e38635d8e0012c', class: "custom-area-field" }, "Enter cuisine name", h("input", { key: '663fee3941527c736d953cc993b886746dabab23', type: "text", placeholder: "e.g. Ethiopian", value: this.customArea, onInput: (e) => {
275
+ this.setFieldError('customCategory', this.validateCustomCategory(this.customCategory));
276
+ } }), this.errors.customCategory && h("span", { key: 'd76519fb39dbc17512c63f8923587219c6f126d1', class: "error" }, this.errors.customCategory))), this.area === 'Other' && (h("label", { key: '0b46c004deda4af6a71a503f950f990d340850f8', class: "custom-area-field" }, "Enter cuisine name", h("input", { key: '3f312b2aaa6b0345ac476a0aa7919ce5638f0205', type: "text", placeholder: "e.g. Ethiopian", maxLength: LIMITS.customArea.max, value: this.customArea, onInput: (e) => {
174
277
  this.customArea = e.target.value;
175
- this.clearErrorIf('customArea', !!this.customArea.trim());
176
- } }), this.errors.customArea && h("span", { key: '6e1b7ebfac7166fb1120b0d8f914bb076f5e40ee', class: "error" }, this.errors.customArea))))), h("div", { key: 'fe85b73291a5ee955d069f20ddf33835d09b95bd', class: "field" }, h("span", { key: 'fd010d7b12306bd4306caf03ebd972feb1e0ebb9' }, "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: '8168f7e7f4bef8be1e74d1adf377b5471701c0ff', type: "button", class: "add-btn", onClick: this.addIngredient }, "+ Add ingredient"), this.errors.ingredients && h("span", { key: '3e35a8fe02833b27a333fa157548b88b3f5e3842', class: "error" }, this.errors.ingredients)), h("label", { key: 'c5c583cba5363ce35e6d41f95917bc49e3cca02d' }, "Instructions", h("textarea", { key: '514c219989a01cf1ad77739bd6c0df066362c689', rows: 5, onInput: (e) => {
278
+ this.setFieldError('customArea', this.validateCustomArea(this.customArea));
279
+ } }), this.errors.customArea && h("span", { key: 'a7d0487409a2f2338fd32ce23770da1fff318189', class: "error" }, this.errors.customArea))))), h("div", { key: '2af4f405001d202a86c0e94426db89b46d4af0c5', class: "field" }, h("span", { key: '1cc25f9cacb59981f1c8b6faf5c7669693454d86' }, "Ingredients ", h("span", { key: '12463cc84c7df68329044f6e00bbf489dc02efbc', class: "required" }, "*")), this.ingredients.map((ing, i) => (h("div", { class: "ingredient-row" }, h("input", { type: "text", maxLength: LIMITS.ingredient.max, 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: 'faadffd1444fa729327bb44e2ba430a38fb8d0f2', type: "button", class: "add-btn", onClick: this.addIngredient }, "+ Add ingredient"), this.errors.ingredients && h("span", { key: '93e3101aa6f8aa6adb28a5cb121cc2f735bdf3cb', class: "error" }, this.errors.ingredients)), h("label", { key: '142d68b899f6264366351ff0edefac0755f5c021' }, h("span", { key: 'd0469328a9f808854e2bc1daeb2df3a2562300e1' }, "Instructions ", h("span", { key: 'df0b49c132848b889adc1a070a17d90505f51f9b', class: "required" }, "*"), " "), h("textarea", { key: '790e5cb9c24947648df27697c7148636e2862bd5', rows: 5, maxLength: LIMITS.instructions.max, onInput: (e) => {
177
280
  this.instructions = e.target.value;
178
- this.clearErrorIf('instructions', !!this.instructions.trim());
179
- } }, this.instructions), this.errors.instructions && h("span", { key: '89881e3b0d4f864f7ccc835d86b094f367b54b2e', class: "error" }, this.errors.instructions)), h("div", { key: 'b9a812287af44062e1c68034881ba62321b0e660', class: "footer" }, h("slot", { key: 'a8307179d95cfd713d8f5fbe6e996fefb1d54907', name: "footer" }), h("button", { key: 'aa75decaf67c74a183af182864a37a6cd37b56ff', type: "button", class: "cancel-btn", onClick: () => this.cancel.emit() }, "Cancel"), h("button", { key: '13ea35b114f1a7721347f986c6c4e64968583025', type: "submit", class: "save-btn" }, "Save Recipe"))));
281
+ this.setFieldError('instructions', this.validateInstructions(this.instructions));
282
+ } }, this.instructions), h("span", { key: 'ae15de2b6a210955334f16fcc0040a739bf62f31', class: "char-count" }, this.instructions.trim().length, "/", LIMITS.instructions.max), this.errors.instructions && h("span", { key: 'db148c0b5347d6a899adb4daf18a59ea19221e7f', class: "error" }, this.errors.instructions)), h("div", { key: 'b42405e2778e862115d5841843a4e63bf5ad5e7a', class: "footer" }, h("slot", { key: 'c4a8533636d1dd5fac2b391e36314569f05a4d01', name: "footer" }), h("button", { key: '4ea910ae3983fcdb133dbdc38ad122b607eb3d5c', type: "button", class: "cancel-btn", onClick: () => this.cancel.emit() }, "Cancel"), h("button", { key: 'a2ea39907bba8f4c35b8f2eaaa501f31bdf17130', type: "submit", class: "save-btn" }, "Save Recipe"))));
180
283
  }
181
284
  static get is() { return "recipe-form"; }
182
285
  static get encapsulation() { return "shadow"; }
@@ -8,7 +8,7 @@ export class RecipeRating {
8
8
  }
9
9
  render() {
10
10
  const stars = Array.from({ length: this.max }, (_, i) => i + 1);
11
- return (h("div", { key: '901887f72d31a470b1264d427806ea6a6184db12', 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")))));
11
+ return (h("div", { key: '47d3b89bf17f95c679f7931ee61d7eea4fc5c556', 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")))));
12
12
  }
13
13
  static get is() { return "recipe-rating"; }
14
14
  static get encapsulation() { return "shadow"; }
@@ -31,8 +31,8 @@ export class RecipeSearchBar {
31
31
  this.internalValue = newVal;
32
32
  }
33
33
  render() {
34
- return (h("div", { key: 'd984cb3aeccf96cdedb62c741956b3d12615d884', class: "wrap" }, h("input", { key: 'c250c60908a8bb0c7d92da5eafe6ae6a61679067', type: "search", placeholder: this.placeholder, value: this.internalValue, onInput: this.onInput, onKeyDown: this.onKeyDown }), this.showSearchButton &&
35
- h("button", { key: '6e1dfe1e6b764d9166dbcdad615f177bcd7d8d8b', onClick: () => this.search.emit({ value: this.internalValue }), "aria-label": "Search" }, "\uD83D\uDD0D")));
34
+ return (h("div", { key: 'd4be8af300e73f7a6526ec48993db0259aafcbe2', class: "wrap" }, h("input", { key: '896908d0b0e10212eae53284d7a6fad79f34c5de', type: "search", placeholder: this.placeholder, value: this.internalValue, onInput: this.onInput, onKeyDown: this.onKeyDown }), this.showSearchButton &&
35
+ h("button", { key: '963394555e1febf733b50153e82858077021800c', onClick: () => this.search.emit({ value: this.internalValue }), "aria-label": "Search" }, "\uD83D\uDD0D")));
36
36
  }
37
37
  static get is() { return "recipe-search-bar"; }
38
38
  static get encapsulation() { return "shadow"; }
@@ -1 +1 @@
1
- import{A as o,d as r}from"./p-BBMv9wjr.js";const s=o,p=r;export{s as AppToast,p as defineCustomElement}
1
+ import{A as o,d as s}from"./p-BjJGcNvA.js";const p=o,r=s;export{p as AppToast,r as defineCustomElement}
@@ -1 +1 @@
1
- export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-BJU3HdRm.js";export{R as RecipeCard}from"./p-CayP90fv.js";export{R as RecipeSearchBar}from"./p-DElrp5qn.js";export{R as RecipeFilterBar}from"./p-8ogxleRP.js";export{R as RecipeRating}from"./p-B_OSW9IJ.js";export{R as RecipeForm}from"./p-CcOtmzb-.js";export{M as MealPlanDay}from"./p-B37vIJ1j.js";export{A as AppModal}from"./p-Dp1sKtrN.js";export{A as AppToast}from"./p-BBMv9wjr.js";
1
+ export{g as getAssetPath,r as render,s as setAssetPath,a as setNonce,b as setPlatformOptions}from"./p-BJU3HdRm.js";export{R as RecipeCard}from"./p-ltkIKdRy.js";export{R as RecipeSearchBar}from"./p-BWjrK8cx.js";export{R as RecipeFilterBar}from"./p-2Oa3Ks4e.js";export{R as RecipeRating}from"./p-HEDJ6pyO.js";export{R as RecipeForm}from"./p-DPXKwfGM.js";export{M as MealPlanDay}from"./p-B37vIJ1j.js";export{A as AppModal}from"./p-Dp1sKtrN.js";export{A as AppToast}from"./p-BjJGcNvA.js";
@@ -0,0 +1 @@
1
+ import{t as s,p as t,H as e,c as i,h as r}from"./p-BJU3HdRm.js";const c=t(class extends e{constructor(s){super(),!1!==s&&this.__registerHost(),this.__attachShadow(),this.filterChange=i(this,"filterChange"),this.categories=[],this.active=""}select(s){this.filterChange.emit({value:s})}render(){const s=Array.isArray(this.categories)?this.categories:[];return r("div",{key:"c19ffd90b0eff5c4b9b06602c9432b1210cc27f7",class:"wrap"},r("button",{key:"3a629b0e211a6b9ab80b015da2d4be44c85f59a1",class:{chip:!0,active:""===this.active},onClick:()=>this.select("")},"All"),s.map((s=>r("button",{class:{chip:!0,active:this.active===s},onClick:()=>this.select(s)},s))))}static get style(){return":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}"}},[1,"recipe-filter-bar",{categories:[16],active:[1]}]);function a(){"undefined"!=typeof customElements&&["recipe-filter-bar"].forEach((t=>{"recipe-filter-bar"===t&&(customElements.get(s(t))||customElements.define(s(t),c))}))}a();export{c as R,a as d}
@@ -1 +1 @@
1
- import{t as e,p as t,H as s,c as i,h as a}from"./p-BJU3HdRm.js";const r=t(class extends s{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.search=i(this,"search"),this.placeholder="Search recipes...",this.value="",this.debounceMs=350,this.showSearchButton=!0,this.internalValue="",this.onInput=e=>{this.internalValue=e.target.value,clearTimeout(this.timer),this.timer=setTimeout((()=>{this.search.emit({value:this.internalValue})}),this.debounceMs)},this.onKeyDown=e=>{"Enter"===e.key&&(clearTimeout(this.timer),this.search.emit({value:this.internalValue}))}}componentWillLoad(){this.internalValue=this.value}onValuePropChange(e){this.internalValue=e}render(){return a("div",{key:"d984cb3aeccf96cdedb62c741956b3d12615d884",class:"wrap"},a("input",{key:"c250c60908a8bb0c7d92da5eafe6ae6a61679067",type:"search",placeholder:this.placeholder,value:this.internalValue,onInput:this.onInput,onKeyDown:this.onKeyDown}),this.showSearchButton&&a("button",{key:"6e1dfe1e6b764d9166dbcdad615f177bcd7d8d8b",onClick:()=>this.search.emit({value:this.internalValue}),"aria-label":"Search"},"🔍"))}static get watchers(){return{value:[{onValuePropChange:0}]}}static get style(){return":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}"}},[1,"recipe-search-bar",{placeholder:[1],value:[1],debounceMs:[2,"debounce-ms"],showSearchButton:[4,"show-search-button"],internalValue:[32]},void 0,{value:[{onValuePropChange:0}]}]);function o(){"undefined"!=typeof customElements&&["recipe-search-bar"].forEach((t=>{"recipe-search-bar"===t&&(customElements.get(e(t))||customElements.define(e(t),r))}))}o();export{r as R,o as d}
1
+ import{t as e,p as t,H as s,c as i,h as a}from"./p-BJU3HdRm.js";const r=t(class extends s{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.search=i(this,"search"),this.placeholder="Search recipes...",this.value="",this.debounceMs=350,this.showSearchButton=!0,this.internalValue="",this.onInput=e=>{this.internalValue=e.target.value,clearTimeout(this.timer),this.timer=setTimeout((()=>{this.search.emit({value:this.internalValue})}),this.debounceMs)},this.onKeyDown=e=>{"Enter"===e.key&&(clearTimeout(this.timer),this.search.emit({value:this.internalValue}))}}componentWillLoad(){this.internalValue=this.value}onValuePropChange(e){this.internalValue=e}render(){return a("div",{key:"d4be8af300e73f7a6526ec48993db0259aafcbe2",class:"wrap"},a("input",{key:"896908d0b0e10212eae53284d7a6fad79f34c5de",type:"search",placeholder:this.placeholder,value:this.internalValue,onInput:this.onInput,onKeyDown:this.onKeyDown}),this.showSearchButton&&a("button",{key:"963394555e1febf733b50153e82858077021800c",onClick:()=>this.search.emit({value:this.internalValue}),"aria-label":"Search"},"🔍"))}static get watchers(){return{value:[{onValuePropChange:0}]}}static get style(){return":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}"}},[1,"recipe-search-bar",{placeholder:[1],value:[1],debounceMs:[2,"debounce-ms"],showSearchButton:[4,"show-search-button"],internalValue:[32]},void 0,{value:[{onValuePropChange:0}]}]);function o(){"undefined"!=typeof customElements&&["recipe-search-bar"].forEach((t=>{"recipe-search-bar"===t&&(customElements.get(e(t))||customElements.define(e(t),r))}))}o();export{r as R,o as d}
@@ -1 +1 @@
1
- import{t as s,p as t,H as o,c as e,h as a}from"./p-BJU3HdRm.js";const i=t(class extends o{constructor(s){super(),!1!==s&&this.__registerHost(),this.__attachShadow(),this.dismiss=e(this,"dismiss"),this.message="",this.type="info",this.duration=3e3}componentDidLoad(){this.duration>0&&setTimeout((()=>this.dismiss.emit()),this.duration)}render(){return a("div",{key:"65346b009a5016ad30b1ab85dc9c1d9bbd9b52f1",class:`toast ${this.type}`},a("span",{key:"4bb1144fa221047ab51ab1b40647b07cc792d358"},this.message),a("button",{key:"7dcd351feccbce93b6959cd04fb0372d42099c50",onClick:()=>this.dismiss.emit(),"aria-label":"Dismiss"},"✕"))}static get style(){return":host{font-family:system-ui, sans-serif;display:block}.toast{display:flex;align-items:center;gap:10px;padding:10px 16px;border-radius:8px;color:#fff;font-size:14px;box-shadow:0 4px 12px rgba(0,0,0,0.15)}.toast.success{background:#2ecc71}.toast.error{background:#e74c3c}.toast.info{background:#3498db}button{border:none;background:none;color:inherit;cursor:pointer;margin-left:auto}"}},[1,"app-toast",{message:[1],type:[1],duration:[2]}]);function n(){"undefined"!=typeof customElements&&["app-toast"].forEach((t=>{"app-toast"===t&&(customElements.get(s(t))||customElements.define(s(t),i))}))}n();export{i as A,n as d}
1
+ import{t as s,p as t,H as a,c as e,h as o}from"./p-BJU3HdRm.js";const i=t(class extends a{constructor(s){super(),!1!==s&&this.__registerHost(),this.__attachShadow(),this.dismiss=e(this,"dismiss"),this.message="",this.type="info",this.duration=3e3}componentDidLoad(){this.duration>0&&setTimeout((()=>this.dismiss.emit()),this.duration)}render(){return o("div",{key:"6d17f8582228f7ec27219c9a9a39d5bd76a780dd",class:`toast ${this.type}`},o("span",{key:"c0524ae830e6c3bdd73f1e0561caaf5a4ae73468"},this.message),o("button",{key:"7aab76915df455b48d2ca6a5d9e0985f7bb87907",onClick:()=>this.dismiss.emit(),"aria-label":"Dismiss"},"✕"))}static get style(){return":host{font-family:system-ui, sans-serif;display:block}.toast{display:flex;align-items:center;gap:10px;padding:10px 16px;border-radius:8px;color:#fff;font-size:14px;box-shadow:0 4px 12px rgba(0,0,0,0.15)}.toast.success{background:#2ecc71}.toast.error{background:#e74c3c}.toast.info{background:#3498db}button{border:none;background:none;color:inherit;cursor:pointer;margin-left:auto}"}},[1,"app-toast",{message:[1],type:[1],duration:[2]}]);function n(){"undefined"!=typeof customElements&&["app-toast"].forEach((t=>{"app-toast"===t&&(customElements.get(s(t))||customElements.define(s(t),i))}))}n();export{i as A,n as d}
@@ -0,0 +1 @@
1
+ import{t as e,p as t,H as a,c as s,h as i}from"./p-BJU3HdRm.js";const r=["Beef","Breakfast","Chicken","Dessert","Goat","Lamb","Miscellaneous","Pasta","Pork","Seafood","Side","Starter","Vegan","Vegetarian","Other"],n=["American","British","Chinese","Croatian","Dutch","Egyptian","Filipino","French","Greek","Indian","Irish","Italian","Jamaican","Japanese","Kenyan","Malaysian","Mexican","Moroccan","Polish","Portuguese","Russian","Spanish","Thai","Tunisian","Turkish","Vietnamese","Other"],c=5,o=100,d=20,h=5e3,f=5,l=200,b=5,u=40,p=5,g=40,y=500,m=t(class extends a{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.save=s(this,"save"),this.cancel=s(this,"cancel"),this.categories=r,this.extraCategories=[],this.areas=n,this.extraAreas=[],this.title="",this.image="",this.category="",this.customCategory="",this.area="",this.customArea="",this.ingredients=[""],this.instructions="",this.errors={},this.onSubmit=e=>{if(e.preventDefault(),!this.validate())return;const t="Other"===this.category?this.customCategory.trim():this.category,a="Other"===this.area?this.customArea.trim():this.area,s={id:this.initialData?.id,title:this.title.trim(),image:this.image.trim(),category:t,cuisine:a,ingredients:this.ingredients.map((e=>e.trim())).filter(Boolean),instructions:this.instructions.trim()};this.save.emit(s)},this.addIngredient=()=>{this.ingredients=[...this.ingredients,""]},this.removeIngredient=e=>{const t=this.ingredients.filter(((t,a)=>a!==e));this.ingredients=t,this.setFieldError("ingredients",this.validateIngredients(t))}}componentWillLoad(){this.loadInitialData()}onInitialDataChange(){this.loadInitialData()}mergeList(e,t){const a=[...e.filter((e=>"Other"!==e))];for(const e of t)e&&!a.includes(e)&&a.push(e);return a.push("Other"),a}get fullCategoryList(){return this.mergeList(this.categories,this.extraCategories)}get fullAreaList(){return this.mergeList(this.areas,this.extraAreas)}validateTitle(e){const t=e.trim().length;return 0===t?"Title is required.":t<c?`Title must be at least ${c} characters.`:t>o?`Title must be ${o} characters or fewer.`:null}validateImage(e){return e.trim().length>y?`Image URL must be ${y} characters or fewer.`:null}validateIngredients(e){const t=e.map((e=>e.trim())).filter(Boolean);return 0===t.length?"Add at least one ingredient.":t.some((e=>e.length<f))?`Each ingredient must be at least ${f} characters.`:t.some((e=>e.length>l))?`Each ingredient must be ${l} characters or fewer.`:null}validateInstructions(e){const t=e.trim().length;return 0===t?"Instructions are required.":t<d?`Instructions must be at least ${d} characters — add a bit more detail.`:t>h?`Instructions must be ${h} characters or fewer.`:null}validateCategory(e){return e.trim()?null:"Category is required."}validateCustomCategory(e){const t=e.trim().length;return 0===t?"Enter a category name.":t<b||t>u?`Category name must be ${b}-${u} characters.`:null}validateArea(e){return e.trim()?null:"Cuisine / Area is required."}validateCustomArea(e){const t=e.trim().length;return 0===t?"Enter a cuisine name.":t<p||t>g?`Cuisine name must be ${p}-${g} characters.`:null}setFieldError(e,t){if((this.errors[e]??null)!==t)if(t)this.errors={...this.errors,[e]:t};else{const t={...this.errors};delete t[e],this.errors=t}}loadInitialData(){let e;if("string"==typeof this.initialData&&this.initialData)try{e=JSON.parse(this.initialData)}catch{e=void 0}else this.initialData&&"object"==typeof this.initialData&&(e=this.initialData);if(e){this.title=e.title??"",this.image=e.image??"",this.ingredients=e.ingredients?.length?[...e.ingredients]:[""],this.instructions=e.instructions??"";const t=this.fullCategoryList.filter((e=>"Other"!==e));e.category&&!t.includes(e.category)?(this.category="Other",this.customCategory=e.category):(this.category=e.category??"",this.customCategory="");const a=this.fullAreaList.filter((e=>"Other"!==e));e.cuisine&&!a.includes(e.cuisine)?(this.area="Other",this.customArea=e.cuisine):(this.area=e.cuisine??"",this.customArea=""),this.errors={}}}validate(){const e={},t=this.validateTitle(this.title);t&&(e.title=t);const a=this.validateImage(this.image);a&&(e.image=a);const s=this.validateIngredients(this.ingredients);s&&(e.ingredients=s);const i=this.validateInstructions(this.instructions);i&&(e.instructions=i);const r=this.validateCategory(this.category);if(r)e.category=r;else if("Other"===this.category){const t=this.validateCustomCategory(this.customCategory);t&&(e.customCategory=t)}const n=this.validateArea(this.area);if(n)e.area=n;else if("Other"===this.area){const t=this.validateCustomArea(this.customArea);t&&(e.customArea=t)}return this.errors=e,0===Object.keys(e).length}updateIngredient(e,t){const a=[...this.ingredients];a[e]=t,this.ingredients=a,this.setFieldError("ingredients",this.validateIngredients(a))}render(){return i("form",{key:"f562e5fa4cdd1f49b6b119471bf7773e319423e8",onSubmit:this.onSubmit},i("slot",{key:"0bcfd3c288244952e69b1206449d6046ff9f832b",name:"header"}),i("label",{key:"b94ca99a985de9ab0a4a827854c6e9f3c2c32228"},i("span",{key:"ea3f3a53edb24ec2a8421c2b97fee17877fa507a"},"Title ",i("span",{key:"ef3093980bc013d65787ae4a0e04c5a86564433e",class:"required"},"*")),i("input",{key:"f9748efc0027f3783d2961d11524c59ca58121fe",type:"text",maxLength:o,value:this.title,onInput:e=>{this.title=e.target.value,this.setFieldError("title",this.validateTitle(this.title))}}),i("span",{key:"0ac83523c019253560b219137390b8fdd3626323",class:"char-count"},this.title.trim().length,"/",o),this.errors.title&&i("span",{key:"2f1a55d2aa593182c3fe6330b3248985331aca2e",class:"error"},this.errors.title)),i("label",{key:"aa7d01fd11b9b4ea0551e8a157a70a666b645720"},"Image URL",i("input",{key:"fd26aabc19167c469419a1714358615f62b125b9",type:"text",maxLength:y,value:this.image,onInput:e=>{this.image=e.target.value,this.setFieldError("image",this.validateImage(this.image))}}),this.errors.image&&i("span",{key:"3f9561c7ee8433cee875c638edcb069baef96b86",class:"error"},this.errors.image)),i("div",{key:"4253c71219c77f40ef8ec811700fa2ffb4e23d55",class:"row"},i("label",{key:"7b892f3e2e87d7ab647050859996f7fcccbec42c"},i("span",{key:"3f63f3f57f9df8673b54c42251282918fee1be69"},"Category ",i("span",{key:"d535dbd85bc96e1af88511f903d96046f23cb283",class:"required"},"*")),i("select",{key:"311099318b5b7e6212eae0c9803adcf2437aa024",onInput:e=>{this.category=e.target.value,this.setFieldError("category",this.validateCategory(this.category)),this.setFieldError("customCategory","Other"!==this.category?null:this.validateCustomCategory(this.customCategory))}},i("option",{key:"9ab9ed037d1cabc6829cf35bf8c77d5ae8ff6518",value:"",selected:""===this.category},"Select a category…"),this.fullCategoryList.map((e=>i("option",{value:e,selected:e===this.category},e)))),this.errors.category&&i("span",{key:"48a10209ff1b6425220388ba0f315899947bb37a",class:"error"},this.errors.category)),i("label",{key:"294923e0daebc5aa126e66c6e8319ca087544dc4"},i("span",{key:"e9fc761f634c20cfc02829fcfeb4767dfaf4c69f"},"Cuisine / Area ",i("span",{key:"52254653a745ef5459e7efd55d688e37324d358e",class:"required"},"*")),i("select",{key:"7938a0a5b0bb6004b29578aee87cf5711e1d935d",onInput:e=>{this.area=e.target.value,this.setFieldError("area",this.validateArea(this.area)),this.setFieldError("customArea","Other"!==this.area?null:this.validateCustomArea(this.customArea))}},i("option",{key:"2e3a43ac518d6d6662cda3c269055cac2d142cd0",value:"",selected:""===this.area},"Select a cuisine…"),this.fullAreaList.map((e=>i("option",{value:e,selected:e===this.area},e)))),this.errors.area&&i("span",{key:"9776cc4829e490a06c34ba6e6d8482fbda1499af",class:"error"},this.errors.area))),("Other"===this.category||"Other"===this.area)&&i("div",{key:"edf7709c6bb1fdf2d11d360dd4bff73067ace685",class:"row"},"Other"===this.category&&i("label",{key:"e1b54a1298d4d9b7f2db16be20095c229fd7d72b",class:"custom-area-field"},"Enter category name",i("input",{key:"e538a1f8da6e55875d7b37538a205bfe09c7e48a",type:"text",placeholder:"e.g. Brunch",maxLength:u,value:this.customCategory,onInput:e=>{this.customCategory=e.target.value,this.setFieldError("customCategory",this.validateCustomCategory(this.customCategory))}}),this.errors.customCategory&&i("span",{key:"d76519fb39dbc17512c63f8923587219c6f126d1",class:"error"},this.errors.customCategory)),"Other"===this.area&&i("label",{key:"0b46c004deda4af6a71a503f950f990d340850f8",class:"custom-area-field"},"Enter cuisine name",i("input",{key:"3f312b2aaa6b0345ac476a0aa7919ce5638f0205",type:"text",placeholder:"e.g. Ethiopian",maxLength:g,value:this.customArea,onInput:e=>{this.customArea=e.target.value,this.setFieldError("customArea",this.validateCustomArea(this.customArea))}}),this.errors.customArea&&i("span",{key:"a7d0487409a2f2338fd32ce23770da1fff318189",class:"error"},this.errors.customArea))),i("div",{key:"2af4f405001d202a86c0e94426db89b46d4af0c5",class:"field"},i("span",{key:"1cc25f9cacb59981f1c8b6faf5c7669693454d86"},"Ingredients ",i("span",{key:"12463cc84c7df68329044f6e00bbf489dc02efbc",class:"required"},"*")),this.ingredients.map(((e,t)=>i("div",{class:"ingredient-row"},i("input",{type:"text",maxLength:l,value:e,onInput:e=>this.updateIngredient(t,e.target.value)}),i("button",{type:"button",onClick:()=>this.removeIngredient(t),"aria-label":"Remove"},"✕")))),i("button",{key:"faadffd1444fa729327bb44e2ba430a38fb8d0f2",type:"button",class:"add-btn",onClick:this.addIngredient},"+ Add ingredient"),this.errors.ingredients&&i("span",{key:"93e3101aa6f8aa6adb28a5cb121cc2f735bdf3cb",class:"error"},this.errors.ingredients)),i("label",{key:"142d68b899f6264366351ff0edefac0755f5c021"},i("span",{key:"d0469328a9f808854e2bc1daeb2df3a2562300e1"},"Instructions ",i("span",{key:"df0b49c132848b889adc1a070a17d90505f51f9b",class:"required"},"*")," "),i("textarea",{key:"790e5cb9c24947648df27697c7148636e2862bd5",rows:5,maxLength:h,onInput:e=>{this.instructions=e.target.value,this.setFieldError("instructions",this.validateInstructions(this.instructions))}},this.instructions),i("span",{key:"ae15de2b6a210955334f16fcc0040a739bf62f31",class:"char-count"},this.instructions.trim().length,"/",h),this.errors.instructions&&i("span",{key:"db148c0b5347d6a899adb4daf18a59ea19221e7f",class:"error"},this.errors.instructions)),i("div",{key:"b42405e2778e862115d5841843a4e63bf5ad5e7a",class:"footer"},i("slot",{key:"c4a8533636d1dd5fac2b391e36314569f05a4d01",name:"footer"}),i("button",{key:"4ea910ae3983fcdb133dbdc38ad122b607eb3d5c",type:"button",class:"cancel-btn",onClick:()=>this.cancel.emit()},"Cancel"),i("button",{key:"a2ea39907bba8f4c35b8f2eaaa501f31bdf17130",type:"submit",class:"save-btn"},"Save Recipe")))}static get watchers(){return{initialData:[{onInitialDataChange:0}]}}static get style(){return":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,select{font-weight:400;padding:8px 10px;border:1px solid #ddd;border-radius:6px;font-size:14px;font-family:inherit;background:#fff;transition:border-color 0.15s ease, box-shadow 0.15s ease}input:focus,textarea:focus,select:focus{outline:none;border-color:rgb(235, 98, 48);box-shadow:0 0 0 3px rgba(255, 107, 53, 0.14)}select{appearance:none;-webkit-appearance:none;-moz-appearance:none;padding-right:34px;cursor:pointer;color:#1f2937;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 10px center;background-size:14px}select:hover{border-color:#bbb}select:invalid,select option[value=\"\"]{color:#9ca3af}.row{display:grid;grid-template-columns:1fr 1fr;gap:14px}@media (max-width: 480px){.row{grid-template-columns:1fr}}.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}.required{color:red}"}},[257,"recipe-form",{initialData:[1,"initial-data"],categories:[16],extraCategories:[16],areas:[16],extraAreas:[16],title:[32],image:[32],category:[32],customCategory:[32],area:[32],customArea:[32],ingredients:[32],instructions:[32],errors:[32]},void 0,{initialData:[{onInitialDataChange:0}]}]);function x(){"undefined"!=typeof customElements&&["recipe-form"].forEach((t=>{"recipe-form"===t&&(customElements.get(e(t))||customElements.define(e(t),m))}))}x();export{m as R,x as d}
@@ -1 +1 @@
1
- import{t as s,p as t,H as e,c as i,h as a}from"./p-BJU3HdRm.js";const r=t(class extends e{constructor(s){super(),!1!==s&&this.__registerHost(),this.__attachShadow(),this.ratingChange=i(this,"ratingChange"),this.value=0,this.readonly=!1,this.max=5}render(){const s=Array.from({length:this.max},((s,t)=>t+1));return a("div",{key:"901887f72d31a470b1264d427806ea6a6184db12",class:"wrap",role:this.readonly?void 0:"radiogroup"},s.map((s=>a("span",{class:{star:!0,filled:s<=this.value,clickable:!this.readonly},onClick:()=>!this.readonly&&this.ratingChange.emit({value:s})},"★"))))}static get style(){return":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}"}},[1,"recipe-rating",{value:[2],readonly:[4],max:[2]}]);function l(){"undefined"!=typeof customElements&&["recipe-rating"].forEach((t=>{"recipe-rating"===t&&(customElements.get(s(t))||customElements.define(s(t),r))}))}l();export{r as R,l as d}
1
+ import{t as s,p as t,H as e,c as i,h as r}from"./p-BJU3HdRm.js";const a=t(class extends e{constructor(s){super(),!1!==s&&this.__registerHost(),this.__attachShadow(),this.ratingChange=i(this,"ratingChange"),this.value=0,this.readonly=!1,this.max=5}render(){const s=Array.from({length:this.max},((s,t)=>t+1));return r("div",{key:"47d3b89bf17f95c679f7931ee61d7eea4fc5c556",class:"wrap",role:this.readonly?void 0:"radiogroup"},s.map((s=>r("span",{class:{star:!0,filled:s<=this.value,clickable:!this.readonly},onClick:()=>!this.readonly&&this.ratingChange.emit({value:s})},"★"))))}static get style(){return":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}"}},[1,"recipe-rating",{value:[2],readonly:[4],max:[2]}]);function l(){"undefined"!=typeof customElements&&["recipe-rating"].forEach((t=>{"recipe-rating"===t&&(customElements.get(s(t))||customElements.define(s(t),a))}))}l();export{a as R,l as d}
@@ -1 +1 @@
1
- import{t as e,p as a,H as s,c as i,h as t}from"./p-BJU3HdRm.js";const r=a(class extends s{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.favoriteToggle=i(this,"favoriteToggle"),this.cardClick=i(this,"cardClick"),this.category="",this.cuisine="",this.favorite=!1,this.onFavoriteClick=e=>{e.stopPropagation(),this.favoriteToggle.emit({favorite:!this.favorite})}}render(){return t("div",{key:"64d7f6fb92a01c650f3972c4c0f0164f1904d959",class:"card",onClick:()=>this.cardClick.emit()},t("div",{key:"b9d014e3fc77346eaa76e6feb15369e9fb622624",class:"image-wrap"},this.image?t("img",{src:this.image,alt:this.recipeTitle}):t("div",{class:"placeholder"}),t("button",{key:"1690299a371310ad5e6b0e0cc238853fb3e9f439",class:{"fav-btn":!0,active:this.favorite},onClick:this.onFavoriteClick,"aria-label":"Toggle favorite"},this.favorite?"★":"☆")),t("div",{key:"52e02f3b16c2ab51209c6812aa94b4642504836a",class:"body"},t("h3",{key:"6df2f0f3146dd8ef5f91cce8e29f0dca00ffbd0a"},this.recipeTitle),t("span",{key:"072d88d380bf578e5a7a967c55d257aa604de23a",class:"types"},this.category&&t("span",{key:"636f5c1a350c93c6111415ac8a25b05301a60899",class:"category"},this.category),this.cuisine&&t("span",{key:"149cc29a60528ecd2734650b52857a00a685cfd6",class:"cuisine"},this.cuisine)),t("div",{key:"2a75de4cbb0eb4a4e37e35a29c48131c3e88080c",class:"actions"},t("slot",{key:"3c249490cef1005c546c6ee139bb70acabc41535",name:"actions"}))))}static get style(){return":host{display:grid;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:rgb(235, 98, 48)}.body{padding:12px 14px}h3{margin:0 0 4px;font-size:15px;line-height:1.3}.types{margin:15px 0;display:flex;gap:9px}.category,.cuisine{font-size:12px;color:#777;text-transform:uppercase;letter-spacing:0.03em;border:1px solid;border-color:rgb(235, 98, 48);color:rgb(235, 98, 48);font-weight:500;border-radius:15px;padding:3px 8px}.actions{margin-top:8px}"}},[257,"recipe-card",{recipeTitle:[1,"recipe-title"],image:[1],category:[1],cuisine:[1],favorite:[4]}]);function c(){"undefined"!=typeof customElements&&["recipe-card"].forEach((a=>{"recipe-card"===a&&(customElements.get(e(a))||customElements.define(e(a),r))}))}c();export{r as R,c as d}
1
+ import{t as e,p as a,H as s,c as i,h as t}from"./p-BJU3HdRm.js";const r=a(class extends s{constructor(e){super(),!1!==e&&this.__registerHost(),this.__attachShadow(),this.favoriteToggle=i(this,"favoriteToggle"),this.cardClick=i(this,"cardClick"),this.category="",this.cuisine="",this.favorite=!1,this.onFavoriteClick=e=>{e.stopPropagation(),this.favoriteToggle.emit({favorite:!this.favorite})}}render(){return t("div",{key:"57023f5420716527cf5bb0a6f775bb62a19f70b6",class:"card",onClick:()=>this.cardClick.emit()},t("div",{key:"137fab4bde9ee40e26217280ba5736b51b96767f",class:"image-wrap"},this.image?t("img",{src:this.image,alt:this.recipeTitle}):t("div",{class:"placeholder"}),t("button",{key:"99b992c4c50082ec42c4b9473b8418d11fe02e10",class:{"fav-btn":!0,active:this.favorite},onClick:this.onFavoriteClick,"aria-label":"Toggle favorite"},this.favorite?"★":"☆")),t("div",{key:"34a0670cb5e1716e6d7b17d4b1ad3aa5e19b614f",class:"body"},t("h3",{key:"c2f5ed25dc9435c10a0b476cebf10e2a7fcfcbd4"},this.recipeTitle),t("span",{key:"e4ee0142c7290110c4cb61bd9a3cae4f2503ca69",class:"types"},this.category&&t("span",{key:"0faf05e5d79a532dd5947f4f51fc44ca75f51844",class:"category"},this.category),this.cuisine&&t("span",{key:"e8aee0b08bed65c359fc589412d89a14ed81f091",class:"cuisine"},this.cuisine)),t("div",{key:"7c000476afc7687f4318abf6df26ba83bac131e2",class:"actions"},t("slot",{key:"e613619696cb0f3967426e1dbb245d23a8913d3d",name:"actions"}))))}static get style(){return":host{display:grid;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:rgb(235, 98, 48)}.body{padding:12px 14px}h3{margin:0 0 4px;font-size:15px;line-height:1.3}.types{margin:15px 0;display:flex;gap:9px}.category,.cuisine{font-size:12px;color:#777;text-transform:uppercase;letter-spacing:0.03em;border:1px solid;border-color:rgb(235, 98, 48);color:rgb(235, 98, 48);font-weight:500;border-radius:15px;padding:3px 8px}.actions{margin-top:8px}"}},[257,"recipe-card",{recipeTitle:[1,"recipe-title"],image:[1],category:[1],cuisine:[1],favorite:[4]}]);function c(){"undefined"!=typeof customElements&&["recipe-card"].forEach((a=>{"recipe-card"===a&&(customElements.get(e(a))||customElements.define(e(a),r))}))}c();export{r as R,c as d}
@@ -1 +1 @@
1
- import{R as o,d as s}from"./p-CayP90fv.js";const a=o,p=s;export{a as RecipeCard,p as defineCustomElement}
1
+ import{R as o,d as s}from"./p-ltkIKdRy.js";const t=o,p=s;export{t as RecipeCard,p as defineCustomElement}
@@ -1 +1 @@
1
- import{R as o,d as s}from"./p-8ogxleRP.js";const p=o,r=s;export{p as RecipeFilterBar,r as defineCustomElement}
1
+ import{R as s,d as o}from"./p-2Oa3Ks4e.js";const a=s,p=o;export{a as RecipeFilterBar,p as defineCustomElement}
@@ -1 +1 @@
1
- import{R as o,d as s}from"./p-CcOtmzb-.js";const t=o,m=s;export{t as RecipeForm,m as defineCustomElement}
1
+ import{R as o,d as s}from"./p-DPXKwfGM.js";const p=o,r=s;export{p as RecipeForm,r as defineCustomElement}
@@ -1 +1 @@
1
- import{R as o,d as s}from"./p-B_OSW9IJ.js";const p=o,r=s;export{p as RecipeRating,r as defineCustomElement}
1
+ import{R as o,d as p}from"./p-HEDJ6pyO.js";const s=o,r=p;export{s as RecipeRating,r as defineCustomElement}
@@ -1 +1 @@
1
- import{R as o,d as p}from"./p-DElrp5qn.js";const r=o,s=p;export{r as RecipeSearchBar,s as defineCustomElement}
1
+ import{R as o,d as r}from"./p-BWjrK8cx.js";const s=o,p=r;export{s as RecipeSearchBar,p as defineCustomElement}