easyorders 0.1.9 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,9 +69,14 @@ my-theme/
69
69
  ├── theme-data.json
70
70
  ├── style.css
71
71
  ├── script.js
72
- └── sections/
73
- ├── header.liquid
74
- ├── footer.liquid
72
+ ├── sections/
73
+ ├── header.liquid
74
+ ├── footer.liquid
75
+ │ └── ...
76
+ └── home-sections/
77
+ ├── <section-key>/
78
+ │ ├── config.json
79
+ │ └── template.liquid
75
80
  └── ...
76
81
  ```
77
82
 
package/dist/bin/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import "dotenv/config";
3
- import readline from "node:readline";
3
+ import { spawn } from "node:child_process";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
- import { spawn } from "node:child_process";
6
+ import readline from "node:readline";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { getCliVersion } from "../server/api.js";
9
9
  const __filename = fileURLToPath(import.meta.url);
@@ -234,6 +234,7 @@ app.get("/theme", (req, res) => {
234
234
  sections: buildSections(),
235
235
  home_sections: buildHomeSections(),
236
236
  theme_data: readJsonFile(path.join(THEME_DIR, "theme-data.json")),
237
+ theme_data_schema: readJsonFile(path.join(THEME_DIR, "schema.json")),
237
238
  config: readJsonFile(path.join(THEME_DIR, "config.json")),
238
239
  style: `${baseUrl}/style.css`,
239
240
  script: `${baseUrl}/script.js`,
@@ -3,20 +3,7 @@
3
3
  "logo": "",
4
4
  "pages": [
5
5
  {
6
- "slug": "terms-and-conditions",
7
- "title": "شروط الاستخدام"
8
- },
9
- {
10
- "slug": "shipping-policy",
11
- "title": "سياسة الشحن"
12
- },
13
- {
14
- "slug": "refund-policy",
15
- "title": "سياسة الاستبدال و الاسترجاع"
16
- },
17
- {
18
- "slug": "privacy-policy",
19
- "title": "سياسات الخصوصية"
6
+ "id": "ADD_PAGE_ID_HERE"
20
7
  }
21
8
  ],
22
9
  "social": [
@@ -47,16 +34,7 @@
47
34
  ],
48
35
  "categories": [
49
36
  {
50
- "name": "Men",
51
- "slug": "womens-accessories"
52
- },
53
- {
54
- "name": "Women",
55
- "slug": "mens-accessories"
56
- },
57
- {
58
- "name": "New Arrival",
59
- "slug": "98"
37
+ "id": "ADD_CATEGORY_ID_HERE"
60
38
  }
61
39
  ],
62
40
  "payment_img": "https://easyorders.fra1.digitaloceanspaces.com/1773958283329740254payment_icons.svg",
@@ -66,45 +44,39 @@
66
44
  "logo": "",
67
45
  "links": [
68
46
  {
69
- "url": "/collections/womens-accessories",
70
- "name": "Men",
71
- "slug": "womens-accessories",
72
- "type": "category"
73
- },
74
- {
75
- "url": "/collections/mens-accessories",
76
- "name": "Women",
77
- "slug": "mens-accessories",
47
+ "id": "ADD_CATEGORY_ID_HERE",
78
48
  "type": "category"
79
49
  },
80
50
  {
81
- "url": "/collections/98",
82
- "name": "New Arrival",
83
- "slug": "98",
84
- "type": "category"
85
- },
86
- {
87
- "url": "/collections/30-off-tree-runner-go-tree-gliders",
88
- "name": "Sale",
89
- "slug": "30-off-tree-runner-go-tree-gliders",
90
- "type": "category"
51
+ "id": "ADD_PAGE_ID_HERE",
52
+ "type": "page"
91
53
  }
92
54
  ],
93
55
  "is_use_config": true
94
56
  },
95
57
  "palette": {
96
- "ft_bg": "",
97
- "hd_bg": "#a4e6b6",
98
- "ann_bg": "#000000",
99
- "ft_text": "",
58
+ "hd_bg": "",
100
59
  "hd_text": "",
60
+ "ann_bg": "",
101
61
  "ann_text": "",
62
+ "ft_bg": "",
63
+ "ft_text": "",
102
64
  "buy_btn_bg": "",
103
- "crt_btn_bg": "",
104
65
  "buy_btn_text": "",
105
- "crt_btn_text": "",
106
66
  "buy_btn_border": "",
107
- "crt_btn_border": ""
67
+ "crt_btn_bg": "",
68
+ "crt_btn_text": "",
69
+ "crt_btn_border": "",
70
+ "product_name_text": "",
71
+ "product_price_text": "",
72
+ "product_sale_price_text": "",
73
+ "body_bg": "",
74
+ "body_text": "",
75
+ "input_border": "",
76
+ "input_bg": "",
77
+ "input_text": "",
78
+ "checkout_form_bg": "",
79
+ "checkout_heading_text": ""
108
80
  },
109
81
  "theme_data": {
110
82
  "hero_slides": [],
@@ -32,35 +32,9 @@
32
32
  "description": "Use tighter spacing between tiles"
33
33
  },
34
34
  {
35
- "name": "tiles",
36
- "type": "object_array",
37
- "description": "Category or collection tiles",
38
- "fields": [
39
- {
40
- "name": "image",
41
- "type": "string",
42
- "default": "",
43
- "description": "Tile image URL"
44
- },
45
- {
46
- "name": "label",
47
- "type": "string",
48
- "default": "Dresses",
49
- "description": "Tile title"
50
- },
51
- {
52
- "name": "tagline",
53
- "type": "string",
54
- "default": "Fluid lines",
55
- "description": "Short line under the title"
56
- },
57
- {
58
- "name": "url",
59
- "type": "string",
60
- "default": "/products",
61
- "description": "Tile link URL"
62
- }
63
- ]
35
+ "name": "mosaic_category_ids",
36
+ "type": "category_multi_select",
37
+ "description": "Categories to show as tiles (search and reorder in the picker). Images and titles load from each category."
64
38
  }
65
39
  ]
66
40
  }
@@ -1,6 +1,8 @@
1
1
  <section
2
- class="category-mosaic{% if section_data.gap_tight %} category-mosaic--tight{% endif %}"
2
+ class="category-mosaic eo-hs{% if section_data.gap_tight %} category-mosaic--tight{% endif %}{% if section_data.mosaic_category_ids == blank or section_data.mosaic_category_ids.size == 0 %} eo-hs--empty{% endif %}"
3
3
  style="--mosaic-cols: {{ section_data.columns | default: '3' }};"
4
+ data-eo-hs-entity="categories"
5
+ data-eo-hs-ids="{{ section_data.mosaic_category_ids | join: ',' | strip }}"
4
6
  >
5
7
  <div class="category-mosaic__head">
6
8
  {% if section_data.title != blank %}
@@ -11,37 +13,15 @@
11
13
  {% endif %}
12
14
  </div>
13
15
 
14
- {% if section_data.tiles and section_data.tiles.size > 0 %}
15
- <div class="category-mosaic__grid">
16
- {% for tile in section_data.tiles %}
17
- <a class="category-mosaic__tile" href="{{ tile.url | default: '#' }}">
18
- <div class="category-mosaic__visual">
19
- {% if tile.image != blank %}
20
- <img
21
- class="category-mosaic__img"
22
- src="{{ tile.image }}"
23
- alt="{{ tile.label }}"
24
- loading="lazy"
25
- width="640"
26
- height="800"
27
- />
28
- {% else %}
29
- <div class="category-mosaic__img-fallback" aria-hidden="true"></div>
30
- {% endif %}
31
- <div class="category-mosaic__shade" aria-hidden="true"></div>
32
- </div>
33
- <div class="category-mosaic__meta">
34
- {% if tile.label != blank %}
35
- <span class="category-mosaic__label">{{ tile.label }}</span>
36
- {% endif %}
37
- {% if tile.tagline != blank %}
38
- <span class="category-mosaic__tagline">{{ tile.tagline }}</span>
39
- {% endif %}
40
- </div>
41
- </a>
16
+ <div class="category-mosaic__grid" data-eo-hs-mount>
17
+ {% if section_data.mosaic_category_ids and section_data.mosaic_category_ids.size > 0 %}
18
+ {% for id in section_data.mosaic_category_ids %}
19
+ <div class="eo-hs-skeleton eo-hs-skeleton--category" aria-hidden="true"></div>
42
20
  {% endfor %}
43
- </div>
44
- {% endif %}
21
+ {% else %}
22
+ <p class="category-mosaic__placeholder">Choose categories in the section settings to build this mosaic.</p>
23
+ {% endif %}
24
+ </div>
45
25
  </section>
46
26
 
47
27
  <style>
@@ -77,6 +57,9 @@
77
57
  grid-template-columns: repeat(var(--mosaic-cols, 3), minmax(0, 1fr));
78
58
  gap: 1.25rem;
79
59
  }
60
+ .category-mosaic__grid.eo-hs-loading {
61
+ opacity: 0.85;
62
+ }
80
63
  @media (max-width: 899px) {
81
64
  .category-mosaic__grid {
82
65
  grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -87,7 +70,26 @@
87
70
  grid-template-columns: 1fr;
88
71
  }
89
72
  }
90
- .category-mosaic__tile {
73
+ .category-mosaic .eo-hs-skeleton--category {
74
+ aspect-ratio: 3 / 4;
75
+ border-radius: 6px;
76
+ background: linear-gradient(110deg, #ece8e2 8%, #f5f2ee 18%, #ece8e2 33%);
77
+ background-size: 200% 100%;
78
+ animation: category-mosaic-shimmer 1.2s ease-in-out infinite;
79
+ }
80
+ .category-mosaic__placeholder {
81
+ grid-column: 1 / -1;
82
+ margin: 0;
83
+ padding: 1.25rem;
84
+ text-align: center;
85
+ color: #7a7368;
86
+ font-size: 0.9rem;
87
+ }
88
+ .category-mosaic.eo-hs--empty .category-mosaic__grid {
89
+ display: flex;
90
+ justify-content: center;
91
+ }
92
+ .category-mosaic .eo-hs-card--category {
91
93
  position: relative;
92
94
  display: block;
93
95
  text-decoration: none;
@@ -97,55 +99,57 @@
97
99
  background: #ece8e1;
98
100
  transition: transform 0.25s ease, box-shadow 0.25s ease;
99
101
  }
100
- .category-mosaic__tile:hover {
102
+ .category-mosaic .eo-hs-card--category:hover {
101
103
  transform: translateY(-4px);
102
104
  box-shadow: 0 22px 50px rgba(20, 18, 24, 0.18);
103
105
  }
104
- .category-mosaic__visual {
105
- position: relative;
106
+ .category-mosaic .eo-hs-card--category .eo-hs-card__media {
107
+ display: block;
106
108
  aspect-ratio: 3 / 4;
109
+ background: #ece8e2;
107
110
  overflow: hidden;
108
111
  }
109
- .category-mosaic__img,
110
- .category-mosaic__img-fallback {
112
+ .category-mosaic .eo-hs-card--category .eo-hs-card__media--empty {
113
+ min-height: 200px;
114
+ }
115
+ .category-mosaic .eo-hs-card--category .eo-hs-card__media img {
111
116
  width: 100%;
112
117
  height: 100%;
113
118
  object-fit: cover;
114
119
  display: block;
115
120
  transition: transform 0.45s ease;
116
121
  }
117
- .category-mosaic__img-fallback {
118
- background: linear-gradient(160deg, #d4cec4, #a39a8f);
119
- }
120
- .category-mosaic__tile:hover .category-mosaic__img {
122
+ .category-mosaic .eo-hs-card--category:hover .eo-hs-card__media img {
121
123
  transform: scale(1.04);
122
124
  }
123
- .category-mosaic__shade {
124
- position: absolute;
125
- inset: 0;
126
- background: linear-gradient(to top, rgba(12, 10, 14, 0.72), transparent 55%);
127
- pointer-events: none;
128
- }
129
- .category-mosaic__meta {
125
+ .category-mosaic .eo-hs-card--category .eo-hs-card__body {
130
126
  position: absolute;
131
127
  left: 0;
132
128
  right: 0;
133
129
  bottom: 0;
134
130
  padding: 1.25rem 1.1rem 1.1rem;
135
- display: flex;
136
- flex-direction: column;
137
- gap: 0.2rem;
138
- color: #fff;
131
+ background: linear-gradient(to top, rgba(12, 10, 14, 0.72), transparent 55%);
139
132
  }
140
- .category-mosaic__label {
133
+ .category-mosaic .eo-hs-card--category .eo-hs-card__title {
141
134
  font-size: 1.05rem;
142
135
  font-weight: 600;
143
136
  letter-spacing: 0.02em;
137
+ color: #fff;
138
+ }
139
+ .category-mosaic .eo-hs-error {
140
+ grid-column: 1 / -1;
141
+ margin: 0;
142
+ padding: 1rem;
143
+ text-align: center;
144
+ font-size: 0.88rem;
145
+ color: #8b3225;
144
146
  }
145
- .category-mosaic__tagline {
146
- font-size: 0.82rem;
147
- opacity: 0.88;
148
- letter-spacing: 0.04em;
149
- text-transform: uppercase;
147
+ @keyframes category-mosaic-shimmer {
148
+ 0% {
149
+ background-position: 100% 0;
150
+ }
151
+ 100% {
152
+ background-position: -100% 0;
153
+ }
150
154
  }
151
155
  </style>
@@ -4,9 +4,9 @@
4
4
  "section_schema": [
5
5
  {
6
6
  "name": "image",
7
- "type": "string",
7
+ "type": "image",
8
8
  "default": "",
9
- "description": "Feature image URL"
9
+ "description": "Feature image"
10
10
  },
11
11
  {
12
12
  "name": "eyebrow",
@@ -33,10 +33,9 @@
33
33
  "description": "Link label"
34
34
  },
35
35
  {
36
- "name": "cta_url",
37
- "type": "string",
38
- "default": "/pages/about",
39
- "description": "Link URL"
36
+ "name": "cta_page_id",
37
+ "type": "page_single_select",
38
+ "description": "Simple page the CTA opens (link URL is resolved on the storefront)"
40
39
  },
41
40
  {
42
41
  "name": "image_position",
@@ -28,7 +28,15 @@
28
28
  <p class="editorial-feature__body">{{ section_data.body }}</p>
29
29
  {% endif %}
30
30
  {% if section_data.cta_label != blank %}
31
- <a class="editorial-feature__link" href="{{ section_data.cta_url | default: '#' }}">
31
+ <a
32
+ class="editorial-feature__link"
33
+ href="#"
34
+ {% if section_data.cta_page_id != blank %}
35
+ data-eo-hs-cta="1"
36
+ data-eo-hs-cta-entity="pages"
37
+ data-eo-hs-cta-id="{{ section_data.cta_page_id }}"
38
+ {% endif %}
39
+ >
32
40
  {{ section_data.cta_label }}
33
41
  <span class="editorial-feature__link-arrow" aria-hidden="true">→</span>
34
42
  </a>
@@ -26,12 +26,6 @@
26
26
  "default": "Join the list",
27
27
  "description": "Submit button label"
28
28
  },
29
- {
30
- "name": "form_action",
31
- "type": "string",
32
- "default": "/pages/contact",
33
- "description": "Form action URL (your ESP or contact page)"
34
- },
35
29
  {
36
30
  "name": "footnote",
37
31
  "type": "string",
@@ -14,15 +14,39 @@
14
14
  <p class="newsletter-luxe__body">{{ section_data.body }}</p>
15
15
  {% endif %}
16
16
 
17
- <form class="newsletter-luxe__form" action="{{ section_data.form_action | default: '/pages/contact' }}" method="get">
17
+ <form
18
+ class="newsletter-luxe__form"
19
+ novalidate
20
+ onsubmit="
21
+ event.preventDefault();
22
+ if (!this.checkValidity()) {
23
+ this.reportValidity();
24
+ return;
25
+ }
26
+ var input = this.elements.email;
27
+ var email = input && input.value ? String(input.value).trim() : '';
28
+ this.dispatchEvent(
29
+ new CustomEvent('footer-subscribe', {
30
+ bubbles: true,
31
+ detail: { email: email }
32
+ })
33
+ );
34
+ this.reset();
35
+ "
36
+ >
18
37
  <input
19
38
  class="newsletter-luxe__input"
20
39
  type="email"
21
40
  name="email"
22
41
  autocomplete="email"
42
+ inputmode="email"
43
+ maxlength="254"
44
+ pattern="[^@\s]+@[^@\s]+\.[^@\s]+"
45
+ title="Valid email required"
46
+ required
47
+ aria-required="true"
23
48
  aria-label="{{ section_data.placeholder | default: 'Email address' }}"
24
49
  placeholder="{{ section_data.placeholder | default: 'Email address' }}"
25
- required
26
50
  />
27
51
  <button class="newsletter-luxe__submit" type="submit">
28
52
  {{ section_data.button_label | default: 'Join the list' }}
@@ -4,9 +4,9 @@
4
4
  "section_schema": [
5
5
  {
6
6
  "name": "image",
7
- "type": "string",
7
+ "type": "image",
8
8
  "default": "",
9
- "description": "Full-width background image URL"
9
+ "description": "Full-width background image"
10
10
  },
11
11
  {
12
12
  "name": "kicker",
@@ -33,10 +33,9 @@
33
33
  "description": "Primary button label"
34
34
  },
35
35
  {
36
- "name": "primary_url",
37
- "type": "string",
38
- "default": "/products",
39
- "description": "Primary button URL"
36
+ "name": "primary_category_id",
37
+ "type": "category_single_select",
38
+ "description": "Collection the primary button links to (resolved on the storefront)"
40
39
  },
41
40
  {
42
41
  "name": "secondary_label",
@@ -45,10 +44,9 @@
45
44
  "description": "Secondary button label (hidden if empty)"
46
45
  },
47
46
  {
48
- "name": "secondary_url",
49
- "type": "string",
50
- "default": "/pages/lookbook",
51
- "description": "Secondary button URL"
47
+ "name": "secondary_page_id",
48
+ "type": "page_single_select",
49
+ "description": "Simple page the secondary button links to (when label is set)"
52
50
  },
53
51
  {
54
52
  "name": "overlay_strength",
@@ -21,12 +21,28 @@
21
21
  {% endif %}
22
22
  <div class="runway-hero__actions">
23
23
  {% if section_data.primary_label != blank %}
24
- <a class="runway-hero__btn runway-hero__btn--primary" href="{{ section_data.primary_url | default: '/products' }}">
24
+ <a
25
+ class="runway-hero__btn runway-hero__btn--primary"
26
+ href="{% if section_data.primary_category_id != blank %}#{% else %}/products{% endif %}"
27
+ {% if section_data.primary_category_id != blank %}
28
+ data-eo-hs-cta="1"
29
+ data-eo-hs-cta-entity="categories"
30
+ data-eo-hs-cta-id="{{ section_data.primary_category_id }}"
31
+ {% endif %}
32
+ >
25
33
  {{ section_data.primary_label }}
26
34
  </a>
27
35
  {% endif %}
28
36
  {% if section_data.secondary_label != blank %}
29
- <a class="runway-hero__btn runway-hero__btn--ghost" href="{{ section_data.secondary_url | default: '#' }}">
37
+ <a
38
+ class="runway-hero__btn runway-hero__btn--ghost"
39
+ href="#"
40
+ {% if section_data.secondary_page_id != blank %}
41
+ data-eo-hs-cta="1"
42
+ data-eo-hs-cta-entity="pages"
43
+ data-eo-hs-cta-id="{{ section_data.secondary_page_id }}"
44
+ {% endif %}
45
+ >
30
46
  {{ section_data.secondary_label }}
31
47
  </a>
32
48
  {% endif %}
@@ -22,9 +22,9 @@
22
22
  },
23
23
  {
24
24
  "name": "look_image",
25
- "type": "string",
25
+ "type": "image",
26
26
  "default": "",
27
- "description": "Main outfit or editorial image URL"
27
+ "description": "Main outfit or editorial image"
28
28
  },
29
29
  {
30
30
  "name": "look_title",
@@ -67,35 +67,9 @@
67
67
  ]
68
68
  },
69
69
  {
70
- "name": "picks",
71
- "type": "object_array",
72
- "description": "Products or pieces to pair with the look",
73
- "fields": [
74
- {
75
- "name": "image",
76
- "type": "string",
77
- "default": "",
78
- "description": "Product image URL"
79
- },
80
- {
81
- "name": "title",
82
- "type": "string",
83
- "default": "Merino crewneck",
84
- "description": "Product name"
85
- },
86
- {
87
- "name": "price",
88
- "type": "string",
89
- "default": "1,290 MAD",
90
- "description": "Price as shown to shoppers"
91
- },
92
- {
93
- "name": "url",
94
- "type": "string",
95
- "default": "/products",
96
- "description": "Product or collection URL"
97
- }
98
- ]
70
+ "name": "pick_product_ids",
71
+ "type": "product_multi_select",
72
+ "description": "Products to show beside the hero (search and reorder in the picker)"
99
73
  }
100
74
  ]
101
75
  }
@@ -25,7 +25,7 @@
25
25
  loading="lazy"
26
26
  width="900"
27
27
  height="1120"
28
- />
28
+ >
29
29
  {% else %}
30
30
  <div class="shop-the-look__hero-placeholder" aria-hidden="true"></div>
31
31
  {% endif %}
@@ -44,36 +44,21 @@
44
44
  </div>
45
45
  </a>
46
46
 
47
- {% if section_data.picks and section_data.picks.size > 0 %}
48
- <div class="shop-the-look__picks">
49
- {% for pick in section_data.picks %}
50
- <a class="shop-the-look__pick" href="{{ pick.url | default: '#' }}">
51
- <div class="shop-the-look__pick-frame">
52
- {% if pick.image != blank %}
53
- <img
54
- class="shop-the-look__pick-img"
55
- src="{{ pick.image }}"
56
- alt="{{ pick.title }}"
57
- loading="lazy"
58
- width="320"
59
- height="400"
60
- />
61
- {% else %}
62
- <div class="shop-the-look__pick-fallback" aria-hidden="true"></div>
63
- {% endif %}
64
- </div>
65
- <div class="shop-the-look__pick-meta">
66
- {% if pick.title != blank %}
67
- <span class="shop-the-look__pick-title">{{ pick.title }}</span>
68
- {% endif %}
69
- {% if pick.price != blank %}
70
- <span class="shop-the-look__pick-price">{{ pick.price }}</span>
71
- {% endif %}
72
- </div>
73
- </a>
74
- {% endfor %}
47
+ <div
48
+ class="shop-the-look__picks eo-hs{% if section_data.pick_product_ids == blank or section_data.pick_product_ids.size == 0 %} eo-hs--empty{% endif %}"
49
+ data-eo-hs-entity="products"
50
+ data-eo-hs-ids="{{ section_data.pick_product_ids | join: ',' | strip }}"
51
+ >
52
+ <div class="shop-the-look__picks-inner" data-eo-hs-mount>
53
+ {% if section_data.pick_product_ids and section_data.pick_product_ids.size > 0 %}
54
+ {% for id in section_data.pick_product_ids %}
55
+ <div class="eo-hs-skeleton eo-hs-skeleton--product" aria-hidden="true"></div>
56
+ {% endfor %}
57
+ {% else %}
58
+ <p class="shop-the-look__picks-empty">Add products in the section settings to list pieces from this look.</p>
59
+ {% endif %}
75
60
  </div>
76
- {% endif %}
61
+ </div>
77
62
  </div>
78
63
  </section>
79
64
 
@@ -204,27 +189,52 @@
204
189
  color: #141218;
205
190
  }
206
191
  .shop-the-look__picks {
192
+ min-width: 0;
193
+ }
194
+ .shop-the-look__picks-empty {
195
+ margin: 0;
196
+ font-size: 0.88rem;
197
+ line-height: 1.5;
198
+ opacity: 0.75;
199
+ padding: 0.5rem 0;
200
+ }
201
+ .shop-the-look__picks-inner {
207
202
  display: flex;
208
203
  flex-direction: column;
209
204
  gap: 0.85rem;
210
205
  }
206
+ .shop-the-look__picks-inner.eo-hs-loading {
207
+ opacity: 0.85;
208
+ }
211
209
  @media (max-width: 959px) {
212
- .shop-the-look__picks {
210
+ .shop-the-look__picks-inner {
213
211
  flex-flow: row nowrap;
214
212
  overflow-x: auto;
215
213
  padding-bottom: 0.35rem;
216
214
  scroll-snap-type: x mandatory;
217
215
  -webkit-overflow-scrolling: touch;
218
216
  }
219
- .shop-the-look__pick {
217
+ .shop-the-look__picks-inner .eo-hs-card--product {
220
218
  flex: 0 0 min(220px, 72vw);
221
219
  scroll-snap-align: start;
222
220
  }
223
221
  }
224
- .shop-the-look__pick {
222
+ .shop-the-look__picks-inner .eo-hs-skeleton--product {
223
+ min-height: 96px;
224
+ border-radius: 8px;
225
+ background: linear-gradient(110deg, #ece8e2 8%, #f5f2ee 18%, #ece8e2 33%);
226
+ background-size: 200% 100%;
227
+ animation: shop-the-look-shimmer 1.2s ease-in-out infinite;
228
+ }
229
+ .shop-the-look--charcoal .shop-the-look__picks-inner .eo-hs-skeleton--product {
230
+ background: linear-gradient(110deg, #2a2630 8%, #3a3540 18%, #2a2630 33%);
231
+ background-size: 200% 100%;
232
+ }
233
+ .shop-the-look__picks-inner .eo-hs-card--product {
225
234
  display: flex;
226
- gap: 0.85rem;
235
+ flex-direction: row;
227
236
  align-items: center;
237
+ gap: 0.85rem;
228
238
  text-decoration: none;
229
239
  color: inherit;
230
240
  padding: 0.65rem;
@@ -233,52 +243,62 @@
233
243
  border: 1px solid rgba(28, 26, 23, 0.06);
234
244
  transition: background 0.2s ease, transform 0.2s ease;
235
245
  }
236
- .shop-the-look--charcoal .shop-the-look__pick {
246
+ .shop-the-look--charcoal .shop-the-look__picks-inner .eo-hs-card--product {
237
247
  background: rgba(255, 255, 255, 0.05);
238
248
  border-color: rgba(255, 255, 255, 0.08);
239
249
  }
240
- .shop-the-look__pick:hover {
250
+ .shop-the-look__picks-inner .eo-hs-card--product:hover {
241
251
  background: rgba(255, 255, 255, 0.85);
242
252
  transform: translateX(2px);
243
253
  }
244
- .shop-the-look--charcoal .shop-the-look__pick:hover {
254
+ .shop-the-look--charcoal .shop-the-look__picks-inner .eo-hs-card--product:hover {
245
255
  background: rgba(255, 255, 255, 0.1);
246
256
  }
247
- .shop-the-look__pick-frame {
257
+ .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__media {
248
258
  flex-shrink: 0;
249
259
  width: 76px;
250
260
  height: 96px;
251
261
  border-radius: 4px;
252
262
  overflow: hidden;
263
+ aspect-ratio: unset;
264
+ }
265
+ .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__media--empty {
266
+ min-height: 96px;
253
267
  background: #ddd8d0;
254
268
  }
255
- .shop-the-look--charcoal .shop-the-look__pick-frame {
269
+ .shop-the-look--charcoal .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__media--empty {
256
270
  background: #2a2630;
257
271
  }
258
- .shop-the-look__pick-img,
259
- .shop-the-look__pick-fallback {
260
- width: 100%;
261
- height: 100%;
262
- object-fit: cover;
263
- display: block;
264
- }
265
- .shop-the-look__pick-fallback {
266
- background: linear-gradient(160deg, #b0a69a, #7d756c);
267
- }
268
- .shop-the-look__pick-meta {
272
+ .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__body {
269
273
  display: flex;
270
274
  flex-direction: column;
271
275
  gap: 0.2rem;
272
276
  min-width: 0;
277
+ padding: 0;
273
278
  }
274
- .shop-the-look__pick-title {
279
+ .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__title {
275
280
  font-size: 0.9rem;
276
281
  font-weight: 600;
277
282
  line-height: 1.35;
278
283
  }
279
- .shop-the-look__pick-price {
284
+ .shop-the-look__picks-inner .eo-hs-card--product .eo-hs-card__meta {
280
285
  font-size: 0.82rem;
281
286
  opacity: 0.75;
282
287
  letter-spacing: 0.03em;
283
288
  }
289
+ .shop-the-look__picks .eo-hs-error {
290
+ margin: 0;
291
+ font-size: 0.86rem;
292
+ color: #8b3225;
293
+ text-align: center;
294
+ padding: 0.5rem 0;
295
+ }
296
+ @keyframes shop-the-look-shimmer {
297
+ 0% {
298
+ background-position: 100% 0;
299
+ }
300
+ 100% {
301
+ background-position: -100% 0;
302
+ }
303
+ }
284
304
  </style>
@@ -25,9 +25,9 @@
25
25
  "fields": [
26
26
  {
27
27
  "name": "icon",
28
- "type": "string",
28
+ "type": "image",
29
29
  "default": "",
30
- "description": "Optional small icon or logo URL"
30
+ "description": "Optional small icon or logo"
31
31
  },
32
32
  {
33
33
  "name": "title",
@@ -75,6 +75,12 @@
75
75
  "name": "hero_slides",
76
76
  "type": "object_array",
77
77
  "fields": [
78
+ {
79
+ "name": "image",
80
+ "type": "image",
81
+ "default": "",
82
+ "description": "Slide background image"
83
+ },
78
84
  {
79
85
  "name": "title",
80
86
  "type": "string",
@@ -373,6 +373,178 @@
373
373
  }
374
374
  });
375
375
  }
376
+ function h() {
377
+ function m(root) {
378
+ var wrap = root.closest("[data-eo-api-base]");
379
+ if (wrap && wrap.getAttribute("data-eo-api-base")) {
380
+ return wrap.getAttribute("data-eo-api-base").replace(/\/$/, "");
381
+ }
382
+ if (typeof window.__EO_STORE_API_BASE__ === "string" && window.__EO_STORE_API_BASE__) {
383
+ return window.__EO_STORE_API_BASE__.replace(/\/$/, "");
384
+ }
385
+ return "https://api.easyorders.dev/api/v1";
386
+ }
387
+ function p(body) {
388
+ if (Array.isArray(body)) return body;
389
+ if (body && Array.isArray(body.data)) return body.data;
390
+ return [];
391
+ }
392
+ document.querySelectorAll("[data-eo-hs-entity]:not([data-eo-hs-done])").forEach(function (root) {
393
+ var base = m(root);
394
+ var mount = root.querySelector("[data-eo-hs-mount]");
395
+ var idsStr = root.getAttribute("data-eo-hs-ids") || "";
396
+ var ids = idsStr
397
+ .split(",")
398
+ .map(function (s) {
399
+ return s.trim();
400
+ })
401
+ .filter(Boolean);
402
+ if (!base || !mount || ids.length === 0) {
403
+ root.setAttribute("data-eo-hs-done", "1");
404
+ return;
405
+ }
406
+ root.setAttribute("data-eo-hs-done", "1");
407
+ var entity = root.getAttribute("data-eo-hs-entity") || "";
408
+ var path =
409
+ entity === "categories"
410
+ ? "categories"
411
+ : entity === "pages"
412
+ ? "simple-pages"
413
+ : "products";
414
+ var url =
415
+ base.replace(/\/$/, "") +
416
+ "/" +
417
+ path +
418
+ "?filter=id||$in||" +
419
+ ids.join(",") +
420
+ "&limit=" +
421
+ ids.length;
422
+ mount.classList.add("eo-hs-loading");
423
+ fetch(url, { credentials: "omit", headers: { Accept: "application/json" } })
424
+ .then(function (res) {
425
+ return res.ok ? res.json() : Promise.reject(new Error(String(res.status)));
426
+ })
427
+ .then(function (body) {
428
+ var rows = p(body);
429
+ var byId = {};
430
+ rows.forEach(function (row) {
431
+ if (row && row.id != null) byId[String(row.id)] = row;
432
+ });
433
+ var frag = document.createDocumentFragment();
434
+ ids.forEach(function (id) {
435
+ var row = byId[id];
436
+ if (!row) return;
437
+ if (entity === "products") {
438
+ var slug = row.slug || "";
439
+ var name = row.name || "";
440
+ var thumb = row.thumb || "";
441
+ var price =
442
+ row.sale_price != null && row.price != null && row.sale_price < row.price
443
+ ? row.sale_price
444
+ : row.price;
445
+ var el = document.createElement("a");
446
+ el.className = "eo-hs-card eo-hs-card--product";
447
+ el.href = slug ? "/products/" + encodeURIComponent(slug) : "#";
448
+ el.innerHTML =
449
+ (thumb
450
+ ? '<span class="eo-hs-card__media"><img src="' +
451
+ String(thumb).replace(/"/g, "") +
452
+ '" alt="" loading="lazy" width="280" height="350" /></span>'
453
+ : '<span class="eo-hs-card__media eo-hs-card__media--empty"></span>') +
454
+ '<span class="eo-hs-card__body"><span class="eo-hs-card__title">' +
455
+ String(name || "").replace(/</g, "&lt;") +
456
+ "</span>" +
457
+ (price != null
458
+ ? '<span class="eo-hs-card__meta">' + String(price) + "</span>"
459
+ : "") +
460
+ "</span>";
461
+ frag.appendChild(el);
462
+ } else if (entity === "categories") {
463
+ var cslug = row.slug || "";
464
+ var cname = row.name || "";
465
+ var cthumb = row.thumb || "";
466
+ var ca = document.createElement("a");
467
+ ca.className = "eo-hs-card eo-hs-card--category";
468
+ ca.href = cslug ? "/collections/" + encodeURIComponent(cslug) : "#";
469
+ ca.innerHTML =
470
+ (cthumb
471
+ ? '<span class="eo-hs-card__media"><img src="' +
472
+ String(cthumb).replace(/"/g, "") +
473
+ '" alt="" loading="lazy" width="320" height="200" /></span>'
474
+ : '<span class="eo-hs-card__media eo-hs-card__media--empty"></span>') +
475
+ '<span class="eo-hs-card__body"><span class="eo-hs-card__title">' +
476
+ String(cname || "").replace(/</g, "&lt;") +
477
+ "</span></span>";
478
+ frag.appendChild(ca);
479
+ } else {
480
+ var pslug = row.slug || "";
481
+ var ptitle = row.title || "";
482
+ var pa = document.createElement("a");
483
+ pa.className = "eo-hs-card eo-hs-card--page";
484
+ pa.href = pslug ? "/pages/" + encodeURIComponent(pslug) : "#";
485
+ pa.innerHTML =
486
+ '<span class="eo-hs-card__body"><span class="eo-hs-card__title">' +
487
+ String(ptitle || "").replace(/</g, "&lt;") +
488
+ "</span></span>";
489
+ frag.appendChild(pa);
490
+ }
491
+ });
492
+ mount.textContent = "";
493
+ mount.appendChild(frag);
494
+ mount.classList.remove("eo-hs-loading");
495
+ root.classList.remove("eo-hs--empty");
496
+ })
497
+ .catch(function () {
498
+ mount.classList.remove("eo-hs-loading");
499
+ mount.innerHTML =
500
+ '<p class="eo-hs-error" role="status">Unable to load this block. Check your connection or try again later.</p>';
501
+ });
502
+ });
503
+ }
504
+ function d() {
505
+ function b(el) {
506
+ var wrap = el.closest("[data-eo-api-base]");
507
+ if (wrap && wrap.getAttribute("data-eo-api-base")) {
508
+ return wrap.getAttribute("data-eo-api-base").replace(/\/$/, "");
509
+ }
510
+ if (typeof window.__EO_STORE_API_BASE__ === "string" && window.__EO_STORE_API_BASE__) {
511
+ return window.__EO_STORE_API_BASE__.replace(/\/$/, "");
512
+ }
513
+ return "https://api.easyorders.dev/api/v1";
514
+ }
515
+ document.querySelectorAll("a[data-eo-hs-cta]:not([data-eo-hs-cta-done])").forEach(function (a) {
516
+ var id = String(a.getAttribute("data-eo-hs-cta-id") || "").trim();
517
+ var entity = a.getAttribute("data-eo-hs-cta-entity") || "";
518
+ a.setAttribute("data-eo-hs-cta-done", "1");
519
+ if (!id) return;
520
+ var base = b(a);
521
+ var path =
522
+ entity === "categories"
523
+ ? "categories"
524
+ : entity === "pages"
525
+ ? "simple-pages"
526
+ : "products";
527
+ var url =
528
+ base.replace(/\/$/, "") +
529
+ "/" +
530
+ path +
531
+ "?filter=id||$in||" + id + "&limit=1";
532
+ fetch(url, { credentials: "omit", headers: { Accept: "application/json" } })
533
+ .then(function (res) {
534
+ return res.ok ? res.json() : Promise.reject(new Error(String(res.status)));
535
+ })
536
+ .then(function (body) {
537
+ var rows = Array.isArray(body) ? body : body && Array.isArray(body.data) ? body.data : [];
538
+ var row = rows[0];
539
+ if (!row) return;
540
+ var slug = row.slug || "";
541
+ if (entity === "categories" && slug) a.href = "/collections/" + encodeURIComponent(slug);
542
+ else if (entity === "pages" && slug) a.href = "/pages/" + encodeURIComponent(slug);
543
+ else if (entity === "products" && slug) a.href = "/products/" + encodeURIComponent(slug);
544
+ })
545
+ .catch(function () {});
546
+ });
547
+ }
376
548
  e(),
377
549
  t(),
378
550
  a(),
@@ -381,6 +553,8 @@
381
553
  o(),
382
554
  u(),
383
555
  v(),
556
+ h(),
557
+ d(),
384
558
  window.__abGalleryKeyInit ||
385
559
  ((window.__abGalleryKeyInit = !0),
386
560
  document.addEventListener("keydown", function (e) {
@@ -407,6 +581,6 @@
407
581
  l(),
408
582
  s(),
409
583
  new MutationObserver(function () {
410
- e(), t(), a(), n(), r(), o(), u(), v(), i(), c(), l(), s();
584
+ e(), t(), a(), n(), r(), o(), u(), v(), h(), d(), i(), c(), l(), s();
411
585
  }).observe(document.body, { childList: !0, subtree: !0 });
412
586
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyorders",
3
- "version": "0.1.9",
3
+ "version": "0.1.13",
4
4
  "description": "CLI tool for creating and developing Easy Orders themes",
5
5
  "type": "module",
6
6
  "bin": {