Skip to content

Commit 8358d30

Browse files
authored
add matchMedia to responsive design (#32)
* Refactor iteration methods to use for...of loops * add matchMedia support * build dist
1 parent 8552b1e commit 8358d30

7 files changed

Lines changed: 199 additions & 75 deletions

File tree

experimental/responsive-design/dist/app.js

Lines changed: 40 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

experimental/responsive-design/package-lock.json

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

experimental/responsive-design/src/data/recipes.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const recipes = [
66
calories: "500 cals",
77
servingSize: "4 servs",
88
image: "./public/images/spaghetti-carbonara.webp",
9-
tags: ["pasta", "Italian", "carbonara"],
9+
tags: ["pasta", "Italian"],
1010
ingredients: ["200g spaghetti", "100g pancetta", "2 large eggs", "50g pecorino cheese", "50g parmesan", "Black pepper", "Salt"],
1111
steps: ["Cook the spaghetti.", "Fry the pancetta.", "Mix eggs and cheese.", "Combine everything with pasta."],
1212
},
@@ -17,7 +17,7 @@ export const recipes = [
1717
calories: "600 cals",
1818
servingSize: "4 servs",
1919
image: "./public/images/chicken-alfredo.webp",
20-
tags: ["pasta", "Italian", "alfredo"],
20+
tags: ["pasta", "Italian"],
2121
ingredients: ["200g fettuccine", "2 chicken breasts", "100g butter", "200ml heavy cream", "50g parmesan", "Garlic", "Salt", "Pepper"],
2222
steps: ["Cook the fettuccine.", "Cook the chicken.", "Make the Alfredo sauce.", "Combine everything."],
2323
},
@@ -50,7 +50,7 @@ export const recipes = [
5050
calories: "700 cals",
5151
servingSize: "4 servs",
5252
image: "./public/images/margherita-pizza.webp",
53-
tags: ["pizza", "Italian", "vegetarian"],
53+
tags: ["pizza", "Italian"],
5454
ingredients: ["Pizza dough", "Tomato sauce", "200g mozzarella", "Fresh basil", "Olive oil", "Salt"],
5555
steps: ["Prepare the dough.", "Spread the sauce.", "Add cheese and basil.", "Bake the pizza."],
5656
},
@@ -61,7 +61,7 @@ export const recipes = [
6161
calories: "250 cals",
6262
servingSize: "2 servs",
6363
image: "./public/images/caesar-salad.webp",
64-
tags: ["salad", "healthy", "quick"],
64+
tags: ["salad", "healthy"],
6565
ingredients: ["Romaine lettuce", "Croutons", "Caesar dressing", "Parmesan cheese", "Lemon juice", "Salt", "Pepper"],
6666
steps: ["Chop the lettuce.", "Add croutons and cheese.", "Toss with dressing.", "Serve immediately."],
6767
},
@@ -72,7 +72,7 @@ export const recipes = [
7272
calories: "150 cals",
7373
servingSize: "12 servs",
7474
image: "./public/images/chocolate-chip-cookies.webp",
75-
tags: ["dessert", "cookies", "chocolate"],
75+
tags: ["dessert", "cookies"],
7676
ingredients: ["200g flour", "100g butter", "100g sugar", "1 egg", "1 tsp vanilla extract", "100g chocolate chips", "1 tsp baking soda", "Salt"],
7777
steps: ["Preheat the oven to 180°C.", "Mix butter and sugar.", "Add egg and vanilla.", "Mix in dry ingredients.", "Fold in chocolate chips.", "Bake for 10-12 minutes."],
7878
},
@@ -83,7 +83,7 @@ export const recipes = [
8383
calories: "200 cals",
8484
servingSize: "4 servs",
8585
image: "./public/images/greek-salad.webp",
86-
tags: ["salad", "Greek", "healthy"],
86+
tags: ["salad", "Greek"],
8787
ingredients: ["2 cucumbers", "4 tomatoes", "1 red onion", "100g feta cheese", "Olives", "Olive oil", "Lemon juice", "Oregano", "Salt", "Pepper"],
8888
steps: ["Chop the vegetables.", "Mix with olives and feta.", "Dress with olive oil and lemon juice.", "Season with oregano, salt, and pepper."],
8989
},
@@ -94,7 +94,7 @@ export const recipes = [
9494
calories: "350 cals",
9595
servingSize: "4 servs",
9696
image: "./public/images/pancakes.webp",
97-
tags: ["breakfast", "pancakes", "sweet"],
97+
tags: ["breakfast", "pancakes"],
9898
ingredients: ["200g flour", "2 tbsp sugar", "2 tsp baking powder", "1/2 tsp salt", "1 egg", "300ml milk", "2 tbsp melted butter"],
9999
steps: ["Mix dry ingredients.", "Whisk in wet ingredients.", "Cook on a hot griddle until bubbles form.", "Flip and cook until golden brown."],
100100
},
@@ -105,7 +105,7 @@ export const recipes = [
105105
calories: "150 cals",
106106
servingSize: "4 servs",
107107
image: "./public/images/tomato-soup.webp",
108-
tags: ["soup", "vegetarian", "comfort"],
108+
tags: ["soup", "vegetarian"],
109109
ingredients: ["1kg tomatoes", "1 onion", "2 cloves garlic", "500ml vegetable broth", "Fresh basil", "Olive oil", "Salt", "Pepper"],
110110
steps: ["Sauté onions and garlic.", "Add tomatoes and broth.", "Simmer for 20 minutes.", "Blend until smooth.", "Season with salt and pepper.", "Garnish with basil."],
111111
},
@@ -116,7 +116,7 @@ export const recipes = [
116116
calories: "400 cals",
117117
servingSize: "8 servs",
118118
image: "./public/images/lemon-drizzle-cake.webp",
119-
tags: ["dessert", "cake", "lemon"],
119+
tags: ["dessert", "cake"],
120120
ingredients: ["200g flour", "200g sugar", "200g butter", "4 eggs", "2 lemons", "100g powdered sugar", "1 tsp baking powder", "Salt"],
121121
steps: [
122122
"Preheat the oven to 180°C.",
@@ -135,7 +135,7 @@ export const recipes = [
135135
calories: "200 cals",
136136
servingSize: "4 servs",
137137
image: "./public/images/garlic-bread.webp",
138-
tags: ["side", "bread", "garlic"],
138+
tags: ["side", "bread"],
139139
ingredients: ["1 baguette", "100g butter", "4 cloves garlic", "Parsley", "Salt"],
140140
steps: ["Preheat the oven to 200°C.", "Mix softened butter with minced garlic and parsley.", "Slice the baguette and spread the garlic butter.", "Wrap in foil and bake for 10-12 minutes."],
141141
},
@@ -146,7 +146,7 @@ export const recipes = [
146146
calories: "450 cals",
147147
servingSize: "4 servs",
148148
image: "./public/images/thai-green-curry.webp",
149-
tags: ["curry", "Thai", "spicy"],
149+
tags: ["curry", "Thai"],
150150
ingredients: ["400ml coconut milk", "2 tbsp green curry paste", "200g chicken breast", "1 bell pepper", "100g snap peas", "1 tbsp fish sauce", "Basil leaves", "Jasmine rice"],
151151
steps: [
152152
"Cook the jasmine rice.",
@@ -165,7 +165,7 @@ export const recipes = [
165165
calories: "550 cals",
166166
servingSize: "4 servs",
167167
image: "./public/images/beef-stroganoff.webp",
168-
tags: ["beef", "Russian", "comfort"],
168+
tags: ["beef", "comfort"],
169169
ingredients: ["500g beef sirloin", "200g mushrooms", "1 onion", "2 cloves garlic", "200ml sour cream", "2 tbsp flour", "Beef broth", "Egg noodles", "Parsley"],
170170
steps: [
171171
"Cook the egg noodles according to package instructions.",
@@ -185,7 +185,7 @@ export const recipes = [
185185
calories: "600 cals",
186186
servingSize: "4 servs",
187187
image: "./public/images/shrimp-paella.webp",
188-
tags: ["seafood", "Spanish", "rice"],
188+
tags: ["seafood", "rice"],
189189
ingredients: ["300g shrimp", "2 cups paella rice", "1 onion", "1 bell pepper", "2 tomatoes", "4 cloves garlic", "1 tsp saffron threads", "4 cups chicken broth", "Peas", "Lemon wedges"],
190190
steps: [
191191
"Sauté onions and garlic until fragrant.",
@@ -205,7 +205,7 @@ export const recipes = [
205205
calories: "350 cals",
206206
servingSize: "4 servs",
207207
image: "./public/images/quinoa-stuffed-peppers.webp",
208-
tags: ["vegetarian", "healthy", "baked"],
208+
tags: ["vegetarian", "healthy"],
209209
ingredients: ["4 bell peppers", "1 cup quinoa", "2 cups vegetable broth", "1 zucchini", "1 onion", "2 cloves garlic", "1 can black beans", "1 cup corn", "1 tsp cumin", "1 tsp paprika", "1 cup shredded cheese", "Cilantro"],
210210
steps: [
211211
"Preheat the oven to 375°F (190°C).",

experimental/responsive-design/src/lib/components/recipe-card.js

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ class RecipeCard extends LightDOMLitElement {
55
static properties = {
66
recipe: { type: Object },
77
isExpanded: { type: Boolean },
8+
isCompactMode: { type: Boolean },
9+
index: { type: Number },
810
};
911

1012
constructor() {
@@ -15,19 +17,87 @@ class RecipeCard extends LightDOMLitElement {
1517
time: "5 mins",
1618
calories: "200 cals",
1719
servingSize: "2 servs",
18-
tags: ["DeliciousRecipes", "HomeCooking", "FoodLovers"],
20+
tags: ["DeliciousRecipes", "HomeCooking"],
1921
ingredients: ["Ingredient 1", "Ingredient 2", "Ingredient 3"],
2022
steps: ["Step 1", "Step 2", "Step 3"],
2123
};
2224
this.isExpanded = false;
25+
this.isCompactMode = false;
26+
this.index = 0;
27+
}
28+
29+
_getPillColor(tag) {
30+
const colorThemes = {
31+
orange: {
32+
tags: ["italian", "pizza", "pancakes", "breakfast"],
33+
classes: "bg-orange-100 text-orange-800 border-orange-200",
34+
},
35+
red: {
36+
tags: ["mexican", "tacos", "beef"],
37+
classes: "bg-red-100 text-red-800 border-red-200",
38+
},
39+
yellow: {
40+
tags: ["thai", "cookies", "curry"],
41+
classes: "bg-yellow-100 text-yellow-800 border-yellow-200",
42+
},
43+
green: {
44+
tags: ["salad", "vegetarian", "stir fry"],
45+
classes: "bg-green-100 text-green-800 border-green-200",
46+
},
47+
amber: {
48+
tags: ["soup", "bread", "comfort"],
49+
classes: "bg-amber-100 text-amber-800 border-amber-200",
50+
},
51+
blue: {
52+
tags: ["greek", "pasta"],
53+
classes: "bg-blue-100 text-blue-800 border-blue-200",
54+
},
55+
pink: {
56+
tags: ["dessert"],
57+
classes: "bg-pink-100 text-pink-800 border-pink-200",
58+
},
59+
rose: {
60+
tags: ["cake"],
61+
classes: "bg-rose-100 text-rose-800 border-rose-200",
62+
},
63+
purple: {
64+
tags: ["spanish"],
65+
classes: "bg-purple-100 text-purple-800 border-purple-200",
66+
},
67+
cyan: {
68+
tags: ["seafood"],
69+
classes: "bg-cyan-100 text-cyan-800 border-cyan-200",
70+
},
71+
emerald: {
72+
tags: ["healthy"],
73+
classes: "bg-emerald-100 text-emerald-800 border-emerald-200",
74+
},
75+
gray: {
76+
tags: ["side"],
77+
classes: "bg-gray-100 text-gray-800 border-gray-200",
78+
},
79+
};
80+
81+
const lowerTag = tag.toLowerCase();
82+
83+
for (const theme of Object.values(colorThemes)) {
84+
if (theme.tags.includes(lowerTag))
85+
return theme.classes;
86+
}
87+
88+
return "bg-slate-100 text-slate-800 border-slate-200";
2389
}
2490

2591
_toggleExpand() {
2692
this.isExpanded = !this.isExpanded;
2793
this.dispatchEvent(
28-
new Event("toggle-expand", {
94+
new CustomEvent("toggle-expand", {
2995
bubbles: true,
3096
composed: true,
97+
detail: {
98+
index: this.index,
99+
isExpanded: this.isExpanded,
100+
},
31101
})
32102
);
33103
}
@@ -62,7 +132,32 @@ class RecipeCard extends LightDOMLitElement {
62132
`;
63133
}
64134

65-
render() {
135+
_compactCard() {
136+
return html`
137+
<div class="relative overflow-hidden rounded-lg bg-white shadow-sm hover:shadow-md">
138+
<div class="flex items-center space-x-3 p-3">
139+
<img src="${this.recipe.image}" alt="${this.recipe.text}" class="h-16 w-16 flex-shrink-0 rounded-lg object-cover" />
140+
<div class="min-w-0 flex-1">
141+
<h3 class="truncate text-sm font-medium text-gray-900">${this.recipe.text}</h3>
142+
<div class="mt-1 flex items-center space-x-2 text-xs text-gray-500">
143+
<span>${this.recipe.time}</span>
144+
<span></span>
145+
<span>${this.recipe.calories}</span>
146+
<span></span>
147+
<span>${this.recipe.servingSize}</span>
148+
</div>
149+
<p class="mt-1 truncate text-xs text-gray-600">${this.recipe.description}</p>
150+
<!-- Tags in compact mode -->
151+
<div class="mt-2 flex flex-wrap gap-1">${this.recipe.tags.map((tag) => html`<span class="${this._getPillColor(tag)} inline-block rounded-full border px-2 py-0.5 text-xs font-medium">${tag}</span> `)}</div>
152+
</div>
153+
<button class="show-more-btn flex-shrink-0 text-sm text-blue-500 hover:text-blue-700" @click="${this._toggleExpand}">${this.isExpanded ? "Less" : "More"}</button>
154+
</div>
155+
${this.isExpanded ? this._getExpandedTemplate() : ""}
156+
</div>
157+
`;
158+
}
159+
160+
_gridCard() {
66161
return html`
67162
<div class="grid-rows-subgrid row-span-6 grid rounded-lg bg-gradient-to-br from-blue-50 to-green-50 text-left shadow-md">
68163
<img src="${this.recipe.image}" alt="${this.recipe.text}" class="row-start-1 h-24 w-full rounded-t-lg object-cover" />
@@ -76,13 +171,17 @@ class RecipeCard extends LightDOMLitElement {
76171
</div>
77172
<p class="text-pretty row-start-4 truncate px-2 py-1 text-xs text-gray-600">${this.recipe.description}</p>
78173
<div class="absolute -top-4 left-0 right-0 flex justify-center space-x-2 p-2">
79-
${this.recipe.tags.map((tag) => html`<span class="inline-flex items-center rounded-md bg-orange-100 px-1 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-orange-500/10">${tag}</span> `)}
174+
${this.recipe.tags.map((tag) => html`<span class="${this._getPillColor(tag)} inline-flex items-center rounded-full border p-1 text-xs font-medium">${tag}</span> `)}
80175
</div>
81176
${this.isExpanded ? this._getExpandedTemplate() : ""}
82177
<button @click="${this._toggleExpand}" class="show-more-btn w-28 justify-self-end border-none bg-transparent p-1 text-sm text-blue-400 hover:text-blue-900">${this.isExpanded ? "Show Less" : "Show More..."}</button>
83178
</div>
84179
`;
85180
}
181+
182+
render() {
183+
return this.isCompactMode ? this._compactCard() : this._gridCard();
184+
}
86185
}
87186

88187
customElements.define("recipe-card", RecipeCard);

experimental/responsive-design/src/lib/components/recipe-grid.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,45 @@ class RecipeGrid extends LightDOMLitElement {
77
static properties = {
88
_expandedCardIndices: { type: Array },
99
recipes: { type: Array },
10+
isCompactLayout: { type: Boolean },
1011
};
1112

1213
constructor() {
1314
super();
1415
this._expandedCardIndices = [];
1516
this.recipes = recipes;
17+
this.isCompactLayout = false;
18+
19+
this._compactQuery = window.matchMedia("(max-width: 640px)");
20+
this._compactQuery.addEventListener("change", this._handleLayoutChange.bind(this));
21+
this.isCompactLayout = this._compactQuery.matches;
22+
}
23+
24+
_handleLayoutChange(event) {
25+
this.isCompactLayout = event.matches;
1626
}
1727

1828
_handleToggleExpand(event) {
19-
const target = event.target;
20-
const cardIndex = parseInt(target.dataset.index);
21-
if (target.isExpanded)
22-
this._expandedCardIndices = [...this._expandedCardIndices, cardIndex];
29+
const { index, isExpanded } = event.detail;
30+
if (isExpanded)
31+
this._expandedCardIndices = [...this._expandedCardIndices, index];
2332
else
24-
this._expandedCardIndices = this._expandedCardIndices.filter((index) => index !== cardIndex);
33+
this._expandedCardIndices = this._expandedCardIndices.filter((i) => i !== index);
2534
}
2635

2736
_getRecipeCardsTemplate() {
28-
return this.recipes.map(
29-
(recipe, index) => html`
30-
<recipe-card
31-
class="${this._expandedCardIndices.includes(index) ? "col-span-2 lg:col-span-2" : ""} grid-rows-subgrid relative row-span-6 mt-1 grid gap-0 rounded-lg bg-white text-left shadow-md hover:shadow-lg"
32-
.recipe="${recipe}"
33-
data-index="${index}"
34-
></recipe-card>
35-
`
36-
);
37+
return this.recipes.map((recipe, index) => {
38+
const isExpanded = this._expandedCardIndices.includes(index);
39+
const gridClasses = this.isCompactLayout
40+
? "relative rounded-lg bg-white text-left shadow-md hover:shadow-lg"
41+
: `${isExpanded ? "col-span-2 lg:col-span-2" : ""} grid-rows-subgrid relative row-span-6 mt-1 grid gap-0 rounded-lg bg-white text-left shadow-md hover:shadow-lg`;
42+
return html` <recipe-card class="${gridClasses}" .recipe="${recipe}" .isExpanded="${isExpanded}" .isCompactMode="${this.isCompactLayout}" .index="${index}" @toggle-expand="${this._handleToggleExpand}"></recipe-card> `;
43+
});
3744
}
3845

3946
render() {
40-
return html` <div class="grid grid-cols-2 gap-5 py-2 pl-2 pr-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 xl:gap-6" @toggle-expand="${this._handleToggleExpand}">${this._getRecipeCardsTemplate()}</div> `;
47+
const containerClass = this.isCompactLayout ? "grid grid-cols-1 gap-3 py-2 pl-2 pr-4" : "grid grid-cols-2 gap-5 py-2 pl-2 pr-4 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 xl:gap-6";
48+
return html` <div class="${containerClass}" @toggle-expand="${this._handleToggleExpand}">${this._getRecipeCardsTemplate()}</div> `;
4149
}
4250
}
4351

0 commit comments

Comments
 (0)