shopify-drag-carousel 1.0.0 → 1.0.4
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 +77 -43
- package/examples/demo-cdn.html +447 -0
- package/examples/demo.html +442 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -18,28 +18,84 @@ A tiny, zero-dependency behavior layer for smooth mouse and touch drag scrolling
|
|
|
18
18
|
npm install shopify-drag-carousel
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
```js
|
|
22
|
+
const DragCarousel = require("shopify-drag-carousel");
|
|
23
|
+
```
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
---
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
<script src="https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/index.js"></script>
|
|
27
|
-
```
|
|
27
|
+
## Quick start (browser)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
1. Add a horizontal row of children (your own layout: flex, grid, etc.).
|
|
30
|
+
2. Put the library on the page, then add class **`drag-carousel`** to the scroll container.
|
|
30
31
|
|
|
31
32
|
```html
|
|
32
33
|
<link
|
|
33
34
|
rel="stylesheet"
|
|
34
35
|
href="https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/style.css"
|
|
35
36
|
/>
|
|
37
|
+
<div class="drag-carousel">
|
|
38
|
+
<div>Slide A</div>
|
|
39
|
+
<div>Slide B</div>
|
|
40
|
+
<div>Slide C</div>
|
|
41
|
+
</div>
|
|
42
|
+
<script src="https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/index.js"></script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
After load, `window.DragCarousel` is available. Auto-init runs on `DOMContentLoaded` for every `.drag-carousel`.
|
|
46
|
+
|
|
47
|
+
**Manual init** (e.g. custom speed or arrows):
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<div id="strip"></div>
|
|
51
|
+
<button type="button" id="prev" aria-label="Previous">←</button>
|
|
52
|
+
<button type="button" id="next" aria-label="Next">→</button>
|
|
53
|
+
<script src="https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/index.js"></script>
|
|
54
|
+
<script>
|
|
55
|
+
new DragCarousel("#strip", {
|
|
56
|
+
speed: 1.5,
|
|
57
|
+
buttons: { prev: "#prev", next: "#next" },
|
|
58
|
+
});
|
|
59
|
+
</script>
|
|
36
60
|
```
|
|
37
61
|
|
|
38
|
-
|
|
62
|
+
Do **not** put `class="drag-carousel"` on the same node if you also call `new DragCarousel` on it with options (avoid double init).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## CDN URLs
|
|
67
|
+
|
|
68
|
+
Use a **major** tag (`@1`) or pin an exact version (`@1.0.0`) in production.
|
|
69
|
+
|
|
70
|
+
| Asset | jsDelivr | unpkg |
|
|
71
|
+
| ----- | -------- | ----- |
|
|
72
|
+
| Script | [cdn.jsdelivr.net/npm/shopify-drag-carousel@1/index.js](https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/index.js) | [unpkg.com/shopify-drag-carousel@1/index.js](https://unpkg.com/shopify-drag-carousel@1/index.js) |
|
|
73
|
+
| Styles (optional) | [cdn.jsdelivr.net/npm/shopify-drag-carousel@1/style.css](https://cdn.jsdelivr.net/npm/shopify-drag-carousel@1/style.css) | [unpkg.com/shopify-drag-carousel@1/style.css](https://unpkg.com/shopify-drag-carousel@1/style.css) |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Live example (bundled demo)
|
|
78
|
+
|
|
79
|
+
The package includes HTML examples under **`examples/`**:
|
|
80
|
+
|
|
81
|
+
| Demo | What it is |
|
|
82
|
+
| ---- | ----------- |
|
|
83
|
+
| **[examples/demo-cdn.html](https://unpkg.com/shopify-drag-carousel@1/examples/demo-cdn.html)** | Full page on **unpkg** — loads the library from the CDN, fetches sample products from [api.escuelajs.co](https://api.escuelajs.co/docs), drag + prev/next. Requires the version to exist on [npm](https://www.npmjs.com/package/shopify-drag-carousel). |
|
|
84
|
+
| **`examples/demo.html`** | Same UI with **relative** script paths — run a static server from the repo root (see below). |
|
|
85
|
+
|
|
86
|
+
**Run the local demo** (clone or `npm install shopify-drag-carousel`, then serve the **package root** over `http://`, not `file://`):
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npm run demo
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Open [http://localhost:5173/examples/demo.html](http://localhost:5173/examples/demo.html) — or [http://localhost:5173/demo.html](http://localhost:5173/demo.html), which redirects to the example folder.
|
|
93
|
+
|
|
94
|
+
---
|
|
39
95
|
|
|
40
96
|
## Shopify (Liquid)
|
|
41
97
|
|
|
42
|
-
|
|
98
|
+
Load the script and optional CSS in your theme (e.g. `theme.liquid` or section assets).
|
|
43
99
|
|
|
44
100
|
```liquid
|
|
45
101
|
<div class="drag-carousel">
|
|
@@ -49,61 +105,39 @@ Drop the script (and optional stylesheet) in your theme—**theme.liquid** asset
|
|
|
49
105
|
</div>
|
|
50
106
|
```
|
|
51
107
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
After **dynamic** updates (e.g. section AJAX), call:
|
|
108
|
+
Style the row/cards yourself (flex, grid, etc.). After **dynamic** HTML (e.g. section AJAX), call:
|
|
55
109
|
|
|
56
110
|
```js
|
|
57
111
|
DragCarousel.initAll();
|
|
58
112
|
```
|
|
59
113
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
**Automatic:** Add class `drag-carousel` to your container; initialization runs when the DOM is ready.
|
|
63
|
-
|
|
64
|
-
**Manual:**
|
|
65
|
-
|
|
66
|
-
```js
|
|
67
|
-
const carousel = new DragCarousel('#my-row', {
|
|
68
|
-
speed: 1.5,
|
|
69
|
-
buttons: {
|
|
70
|
-
prev: '.prev-btn',
|
|
71
|
-
next: '.next-btn',
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Node / CommonJS:**
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
const DragCarousel = require('shopify-drag-carousel');
|
|
80
|
-
new DragCarousel('.drag-carousel');
|
|
81
|
-
```
|
|
114
|
+
---
|
|
82
115
|
|
|
83
116
|
## API
|
|
84
117
|
|
|
85
118
|
### `new DragCarousel(selectorOrElement, options?)`
|
|
86
119
|
|
|
87
|
-
| Option
|
|
88
|
-
|
|
|
89
|
-
| `speed`
|
|
90
|
-
| `buttons` | `object` | `null`
|
|
120
|
+
| Option | Type | Default | Description |
|
|
121
|
+
| ------ | ---- | ------- | ----------- |
|
|
122
|
+
| `speed` | `number` | `1.5` | Multiplier for pointer movement vs. scroll |
|
|
123
|
+
| `buttons` | `object` | `null` | `{ prev: string, next: string }` — any valid `querySelector` string |
|
|
91
124
|
|
|
92
|
-
|
|
125
|
+
Selectors resolve with `document.querySelector` (first match).
|
|
93
126
|
|
|
94
127
|
### `DragCarousel.initAll()`
|
|
95
128
|
|
|
96
|
-
|
|
129
|
+
Initializes every `.drag-carousel` in the document that is not already initialized. Use after injecting new markup.
|
|
97
130
|
|
|
98
131
|
### `DragCarousel.isInitialized(element)`
|
|
99
132
|
|
|
100
|
-
Returns whether the
|
|
133
|
+
Returns whether the element already has carousel behavior.
|
|
134
|
+
|
|
135
|
+
---
|
|
101
136
|
|
|
102
137
|
## Constraints
|
|
103
138
|
|
|
104
|
-
- No React
|
|
105
|
-
-
|
|
106
|
-
- Intended to run directly in the browser via script tag or small bundles
|
|
139
|
+
- No React required; no bundler required for browser usage
|
|
140
|
+
- Intended for direct use via `<script>` or small bundles
|
|
107
141
|
|
|
108
142
|
## License
|
|
109
143
|
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>shopify-drag-carousel — example (CDN)</title>
|
|
7
|
+
<!-- Library from unpkg — no clone or npm install required to try the demo -->
|
|
8
|
+
<link
|
|
9
|
+
rel="stylesheet"
|
|
10
|
+
href="https://unpkg.com/shopify-drag-carousel@1/style.css"
|
|
11
|
+
/>
|
|
12
|
+
<style>
|
|
13
|
+
:root {
|
|
14
|
+
--bg: #f4f2ef;
|
|
15
|
+
--surface: #ffffff;
|
|
16
|
+
--text: #1c1917;
|
|
17
|
+
--muted: #78716c;
|
|
18
|
+
--accent: #0d9488;
|
|
19
|
+
--accent-soft: rgba(13, 148, 136, 0.12);
|
|
20
|
+
--radius: 14px;
|
|
21
|
+
--shadow: 0 4px 20px rgba(28, 25, 23, 0.08);
|
|
22
|
+
--shadow-hover: 0 12px 32px rgba(28, 25, 23, 0.12);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
* {
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body {
|
|
30
|
+
margin: 0;
|
|
31
|
+
min-height: 100vh;
|
|
32
|
+
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
|
|
33
|
+
color: var(--text);
|
|
34
|
+
background: radial-gradient(1200px 600px at 10% -10%, #e0f2f1 0%, transparent 55%),
|
|
35
|
+
radial-gradient(900px 500px at 100% 0%, #fef3c7 0%, transparent 50%), var(--bg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.page {
|
|
39
|
+
max-width: 68rem;
|
|
40
|
+
margin: 0 auto;
|
|
41
|
+
padding: 2.5rem 1.25rem 4rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.hero {
|
|
45
|
+
text-align: center;
|
|
46
|
+
margin-bottom: 2.5rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.hero__eyebrow {
|
|
50
|
+
display: inline-block;
|
|
51
|
+
font-size: 0.75rem;
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
letter-spacing: 0.12em;
|
|
54
|
+
text-transform: uppercase;
|
|
55
|
+
color: var(--accent);
|
|
56
|
+
background: var(--accent-soft);
|
|
57
|
+
padding: 0.35rem 0.75rem;
|
|
58
|
+
border-radius: 999px;
|
|
59
|
+
margin-bottom: 0.75rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.hero h1 {
|
|
63
|
+
font-size: clamp(1.5rem, 4vw, 2rem);
|
|
64
|
+
font-weight: 700;
|
|
65
|
+
letter-spacing: -0.02em;
|
|
66
|
+
margin: 0 0 0.5rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.hero p {
|
|
70
|
+
margin: 0 auto;
|
|
71
|
+
max-width: 36rem;
|
|
72
|
+
color: var(--muted);
|
|
73
|
+
line-height: 1.55;
|
|
74
|
+
font-size: 0.95rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.hero code {
|
|
78
|
+
font-size: 0.85em;
|
|
79
|
+
background: rgba(0, 0, 0, 0.06);
|
|
80
|
+
padding: 0.15rem 0.4rem;
|
|
81
|
+
border-radius: 6px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.section {
|
|
85
|
+
margin-bottom: 2.75rem;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.section__head {
|
|
89
|
+
display: flex;
|
|
90
|
+
align-items: flex-end;
|
|
91
|
+
justify-content: space-between;
|
|
92
|
+
gap: 1rem;
|
|
93
|
+
margin-bottom: 1rem;
|
|
94
|
+
flex-wrap: wrap;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.section__title {
|
|
98
|
+
font-size: 1.125rem;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
margin: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.section__hint {
|
|
104
|
+
font-size: 0.8rem;
|
|
105
|
+
color: var(--muted);
|
|
106
|
+
margin: 0.25rem 0 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.toolbar {
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
gap: 0.5rem;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.toolbar button {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
justify-content: center;
|
|
119
|
+
width: 2.5rem;
|
|
120
|
+
height: 2.5rem;
|
|
121
|
+
border: 1px solid rgba(28, 25, 23, 0.12);
|
|
122
|
+
border-radius: 10px;
|
|
123
|
+
background: var(--surface);
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
color: var(--text);
|
|
126
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
127
|
+
transition: background 0.15s, box-shadow 0.15s, transform 0.15s;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.toolbar button:hover:not(:disabled) {
|
|
131
|
+
background: #fafaf9;
|
|
132
|
+
box-shadow: var(--shadow);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.toolbar button:active:not(:disabled) {
|
|
136
|
+
transform: scale(0.96);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.toolbar button:disabled {
|
|
140
|
+
opacity: 0.35;
|
|
141
|
+
cursor: not-allowed;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Scroll track: flex row; library adds overflow + drag */
|
|
145
|
+
.scroller {
|
|
146
|
+
display: flex;
|
|
147
|
+
gap: 1rem;
|
|
148
|
+
padding: 0.5rem 0.25rem 1rem;
|
|
149
|
+
scroll-padding-inline: 0.25rem;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.scroller-wrap {
|
|
153
|
+
position: relative;
|
|
154
|
+
border-radius: var(--radius);
|
|
155
|
+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(255, 255, 255, 0.72));
|
|
156
|
+
border: 1px solid rgba(28, 25, 23, 0.06);
|
|
157
|
+
box-shadow: var(--shadow);
|
|
158
|
+
padding: 0.75rem 0.5rem;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.product-card {
|
|
162
|
+
flex: 0 0 15rem;
|
|
163
|
+
min-width: 15rem;
|
|
164
|
+
background: var(--surface);
|
|
165
|
+
border-radius: var(--radius);
|
|
166
|
+
overflow: hidden;
|
|
167
|
+
border: 1px solid rgba(28, 25, 23, 0.06);
|
|
168
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
169
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.product-card:hover {
|
|
173
|
+
transform: translateY(-3px);
|
|
174
|
+
box-shadow: var(--shadow-hover);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.product-card__media {
|
|
178
|
+
position: relative;
|
|
179
|
+
aspect-ratio: 4 / 3;
|
|
180
|
+
background: linear-gradient(145deg, #e7e5e4, #d6d3d1);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.product-card__media img {
|
|
184
|
+
width: 100%;
|
|
185
|
+
height: 100%;
|
|
186
|
+
object-fit: cover;
|
|
187
|
+
display: block;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.product-card__badge {
|
|
191
|
+
position: absolute;
|
|
192
|
+
top: 0.65rem;
|
|
193
|
+
left: 0.65rem;
|
|
194
|
+
font-size: 0.65rem;
|
|
195
|
+
font-weight: 600;
|
|
196
|
+
text-transform: uppercase;
|
|
197
|
+
letter-spacing: 0.06em;
|
|
198
|
+
color: #fff;
|
|
199
|
+
background: rgba(28, 25, 23, 0.72);
|
|
200
|
+
backdrop-filter: blur(6px);
|
|
201
|
+
padding: 0.25rem 0.5rem;
|
|
202
|
+
border-radius: 6px;
|
|
203
|
+
max-width: calc(100% - 1.3rem);
|
|
204
|
+
white-space: nowrap;
|
|
205
|
+
overflow: hidden;
|
|
206
|
+
text-overflow: ellipsis;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.product-card__body {
|
|
210
|
+
padding: 0.9rem 1rem 1.1rem;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.product-card__title {
|
|
214
|
+
margin: 0 0 0.35rem;
|
|
215
|
+
font-size: 0.9rem;
|
|
216
|
+
font-weight: 600;
|
|
217
|
+
line-height: 1.35;
|
|
218
|
+
display: -webkit-box;
|
|
219
|
+
-webkit-line-clamp: 2;
|
|
220
|
+
-webkit-box-orient: vertical;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
min-height: 2.4em;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.product-card__desc {
|
|
226
|
+
margin: 0 0 0.65rem;
|
|
227
|
+
font-size: 0.72rem;
|
|
228
|
+
line-height: 1.45;
|
|
229
|
+
color: var(--muted);
|
|
230
|
+
display: -webkit-box;
|
|
231
|
+
-webkit-line-clamp: 2;
|
|
232
|
+
-webkit-box-orient: vertical;
|
|
233
|
+
overflow: hidden;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.product-card__row {
|
|
237
|
+
display: flex;
|
|
238
|
+
align-items: baseline;
|
|
239
|
+
justify-content: space-between;
|
|
240
|
+
gap: 0.5rem;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.product-card__price {
|
|
244
|
+
font-size: 1.05rem;
|
|
245
|
+
font-weight: 700;
|
|
246
|
+
color: var(--accent);
|
|
247
|
+
letter-spacing: -0.02em;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.product-card__id {
|
|
251
|
+
font-size: 0.65rem;
|
|
252
|
+
color: #a8a29e;
|
|
253
|
+
font-variant-numeric: tabular-nums;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#status {
|
|
257
|
+
margin-top: 1.5rem;
|
|
258
|
+
padding: 0.75rem 1rem;
|
|
259
|
+
border-radius: 10px;
|
|
260
|
+
font-size: 0.85rem;
|
|
261
|
+
background: rgba(255, 255, 255, 0.65);
|
|
262
|
+
border: 1px solid rgba(28, 25, 23, 0.08);
|
|
263
|
+
color: var(--muted);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
#status.is-error {
|
|
267
|
+
color: #b91c1c;
|
|
268
|
+
border-color: rgba(185, 28, 28, 0.25);
|
|
269
|
+
background: #fef2f2;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
#status.is-ok {
|
|
273
|
+
color: #0f766e;
|
|
274
|
+
border-color: rgba(13, 148, 136, 0.25);
|
|
275
|
+
background: #f0fdfa;
|
|
276
|
+
}
|
|
277
|
+
</style>
|
|
278
|
+
</head>
|
|
279
|
+
<body>
|
|
280
|
+
<div class="page">
|
|
281
|
+
<header class="hero">
|
|
282
|
+
<span class="hero__eyebrow">Example</span>
|
|
283
|
+
<h1>shopify-drag-carousel</h1>
|
|
284
|
+
<p>
|
|
285
|
+
Products load from
|
|
286
|
+
<code>GET https://api.escuelajs.co/api/v1/products</code>
|
|
287
|
+
— drag the row or use arrows. The library loads from
|
|
288
|
+
<strong>unpkg</strong> (<code>shopify-drag-carousel@1</code>). For local
|
|
289
|
+
dev, use <code>examples/demo.html</code> in the repo instead.
|
|
290
|
+
</p>
|
|
291
|
+
</header>
|
|
292
|
+
|
|
293
|
+
<section class="section" aria-labelledby="sec-manual">
|
|
294
|
+
<div class="section__head">
|
|
295
|
+
<div>
|
|
296
|
+
<h2 class="section__title" id="sec-manual">Manual init + prev / next</h2>
|
|
297
|
+
<p class="section__hint">
|
|
298
|
+
<code>new DragCarousel('#strip-main', { buttons: { … } })</code>
|
|
299
|
+
</p>
|
|
300
|
+
</div>
|
|
301
|
+
<div class="toolbar">
|
|
302
|
+
<button type="button" id="btn-prev" aria-label="Previous products">←</button>
|
|
303
|
+
<button type="button" id="btn-next" aria-label="Next products">→</button>
|
|
304
|
+
</div>
|
|
305
|
+
</div>
|
|
306
|
+
<div class="scroller-wrap">
|
|
307
|
+
<div id="strip-main" class="scroller"></div>
|
|
308
|
+
</div>
|
|
309
|
+
</section>
|
|
310
|
+
|
|
311
|
+
<section class="section" aria-labelledby="sec-auto">
|
|
312
|
+
<div class="section__head">
|
|
313
|
+
<div>
|
|
314
|
+
<h2 class="section__title" id="sec-auto">Auto-init</h2>
|
|
315
|
+
<p class="section__hint">
|
|
316
|
+
Class <code>.drag-carousel</code> — initialized on
|
|
317
|
+
<code>DOMContentLoaded</code>
|
|
318
|
+
</p>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
<div class="scroller-wrap">
|
|
322
|
+
<div id="strip-auto" class="drag-carousel scroller"></div>
|
|
323
|
+
</div>
|
|
324
|
+
</section>
|
|
325
|
+
|
|
326
|
+
<p id="status" role="status"></p>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<script src="https://unpkg.com/shopify-drag-carousel@1/index.js"></script>
|
|
330
|
+
<script>
|
|
331
|
+
const API = "https://api.escuelajs.co/api/v1/products";
|
|
332
|
+
|
|
333
|
+
function truncate(s, n) {
|
|
334
|
+
const t = String(s || "");
|
|
335
|
+
return t.length <= n ? t : t.slice(0, n - 1) + "…";
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function productCard(p) {
|
|
339
|
+
const el = document.createElement("article");
|
|
340
|
+
el.className = "product-card";
|
|
341
|
+
|
|
342
|
+
const media = document.createElement("div");
|
|
343
|
+
media.className = "product-card__media";
|
|
344
|
+
|
|
345
|
+
const img = document.createElement("img");
|
|
346
|
+
img.loading = "lazy";
|
|
347
|
+
const src =
|
|
348
|
+
Array.isArray(p.images) && p.images.length
|
|
349
|
+
? p.images[0]
|
|
350
|
+
: "";
|
|
351
|
+
img.src =
|
|
352
|
+
src ||
|
|
353
|
+
"data:image/svg+xml," +
|
|
354
|
+
encodeURIComponent(
|
|
355
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"><rect fill="#d6d3d1" width="100%" height="100%"/></svg>'
|
|
356
|
+
);
|
|
357
|
+
const title = String(p.title || "");
|
|
358
|
+
img.alt = title;
|
|
359
|
+
|
|
360
|
+
const badge = document.createElement("span");
|
|
361
|
+
badge.className = "product-card__badge";
|
|
362
|
+
badge.textContent = p.category && p.category.name ? String(p.category.name) : "Product";
|
|
363
|
+
|
|
364
|
+
media.append(img, badge);
|
|
365
|
+
|
|
366
|
+
const body = document.createElement("div");
|
|
367
|
+
body.className = "product-card__body";
|
|
368
|
+
|
|
369
|
+
const h = document.createElement("h3");
|
|
370
|
+
h.className = "product-card__title";
|
|
371
|
+
h.textContent = title;
|
|
372
|
+
|
|
373
|
+
const desc = document.createElement("p");
|
|
374
|
+
desc.className = "product-card__desc";
|
|
375
|
+
desc.textContent = truncate(p.description || "", 120);
|
|
376
|
+
|
|
377
|
+
const row = document.createElement("div");
|
|
378
|
+
row.className = "product-card__row";
|
|
379
|
+
|
|
380
|
+
const price = document.createElement("span");
|
|
381
|
+
price.className = "product-card__price";
|
|
382
|
+
const n = Number(p.price);
|
|
383
|
+
price.textContent = Number.isFinite(n) ? "$" + n.toFixed(2) : "";
|
|
384
|
+
|
|
385
|
+
const id = document.createElement("span");
|
|
386
|
+
id.className = "product-card__id";
|
|
387
|
+
id.textContent = "#" + String(p.id != null ? p.id : "");
|
|
388
|
+
|
|
389
|
+
row.append(price, id);
|
|
390
|
+
body.append(h, desc, row);
|
|
391
|
+
el.append(media, body);
|
|
392
|
+
return el;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function fill(el, list) {
|
|
396
|
+
el.replaceChildren();
|
|
397
|
+
list.forEach((p) => el.appendChild(productCard(p)));
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
(async () => {
|
|
401
|
+
const status = document.getElementById("status");
|
|
402
|
+
|
|
403
|
+
if (typeof DragCarousel === "undefined") {
|
|
404
|
+
status.className = "is-error";
|
|
405
|
+
status.textContent =
|
|
406
|
+
"DragCarousel not found. Check that unpkg could load shopify-drag-carousel@1.";
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
status.textContent = "Loading products…";
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
const res = await fetch(API);
|
|
414
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
415
|
+
const all = await res.json();
|
|
416
|
+
if (!Array.isArray(all)) throw new Error("Unexpected JSON");
|
|
417
|
+
|
|
418
|
+
const a = all.slice(0, 12);
|
|
419
|
+
const b = all.slice(12, 24);
|
|
420
|
+
|
|
421
|
+
fill(document.getElementById("strip-main"), a);
|
|
422
|
+
fill(document.getElementById("strip-auto"), b);
|
|
423
|
+
|
|
424
|
+
void new DragCarousel("#strip-main", {
|
|
425
|
+
speed: 1.5,
|
|
426
|
+
buttons: { prev: "#btn-prev", next: "#btn-next" },
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
status.className = "is-ok";
|
|
430
|
+
status.textContent =
|
|
431
|
+
"Loaded " +
|
|
432
|
+
all.length +
|
|
433
|
+
" products (showing " +
|
|
434
|
+
a.length +
|
|
435
|
+
" + " +
|
|
436
|
+
b.length +
|
|
437
|
+
"). Demo loads the package from unpkg — no clone required.";
|
|
438
|
+
} catch (e) {
|
|
439
|
+
status.className = "is-error";
|
|
440
|
+
status.textContent =
|
|
441
|
+
"Could not load the API. Use http:// (e.g. npm run demo), not file://. " +
|
|
442
|
+
(e && e.message ? "(" + e.message + ")" : "");
|
|
443
|
+
}
|
|
444
|
+
})();
|
|
445
|
+
</script>
|
|
446
|
+
</body>
|
|
447
|
+
</html>
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>shopify-drag-carousel — example</title>
|
|
7
|
+
<!-- Library optional CSS (grabbing cursor); behavior also injects base styles -->
|
|
8
|
+
<link rel="stylesheet" href="../style.css" />
|
|
9
|
+
<style>
|
|
10
|
+
:root {
|
|
11
|
+
--bg: #f4f2ef;
|
|
12
|
+
--surface: #ffffff;
|
|
13
|
+
--text: #1c1917;
|
|
14
|
+
--muted: #78716c;
|
|
15
|
+
--accent: #0d9488;
|
|
16
|
+
--accent-soft: rgba(13, 148, 136, 0.12);
|
|
17
|
+
--radius: 14px;
|
|
18
|
+
--shadow: 0 4px 20px rgba(28, 25, 23, 0.08);
|
|
19
|
+
--shadow-hover: 0 12px 32px rgba(28, 25, 23, 0.12);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* {
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body {
|
|
27
|
+
margin: 0;
|
|
28
|
+
min-height: 100vh;
|
|
29
|
+
font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
|
|
30
|
+
color: var(--text);
|
|
31
|
+
background: radial-gradient(1200px 600px at 10% -10%, #e0f2f1 0%, transparent 55%),
|
|
32
|
+
radial-gradient(900px 500px at 100% 0%, #fef3c7 0%, transparent 50%), var(--bg);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.page {
|
|
36
|
+
max-width: 68rem;
|
|
37
|
+
margin: 0 auto;
|
|
38
|
+
padding: 2.5rem 1.25rem 4rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.hero {
|
|
42
|
+
text-align: center;
|
|
43
|
+
margin-bottom: 2.5rem;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.hero__eyebrow {
|
|
47
|
+
display: inline-block;
|
|
48
|
+
font-size: 0.75rem;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
letter-spacing: 0.12em;
|
|
51
|
+
text-transform: uppercase;
|
|
52
|
+
color: var(--accent);
|
|
53
|
+
background: var(--accent-soft);
|
|
54
|
+
padding: 0.35rem 0.75rem;
|
|
55
|
+
border-radius: 999px;
|
|
56
|
+
margin-bottom: 0.75rem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.hero h1 {
|
|
60
|
+
font-size: clamp(1.5rem, 4vw, 2rem);
|
|
61
|
+
font-weight: 700;
|
|
62
|
+
letter-spacing: -0.02em;
|
|
63
|
+
margin: 0 0 0.5rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.hero p {
|
|
67
|
+
margin: 0 auto;
|
|
68
|
+
max-width: 36rem;
|
|
69
|
+
color: var(--muted);
|
|
70
|
+
line-height: 1.55;
|
|
71
|
+
font-size: 0.95rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.hero code {
|
|
75
|
+
font-size: 0.85em;
|
|
76
|
+
background: rgba(0, 0, 0, 0.06);
|
|
77
|
+
padding: 0.15rem 0.4rem;
|
|
78
|
+
border-radius: 6px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.section {
|
|
82
|
+
margin-bottom: 2.75rem;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.section__head {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: flex-end;
|
|
88
|
+
justify-content: space-between;
|
|
89
|
+
gap: 1rem;
|
|
90
|
+
margin-bottom: 1rem;
|
|
91
|
+
flex-wrap: wrap;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.section__title {
|
|
95
|
+
font-size: 1.125rem;
|
|
96
|
+
font-weight: 600;
|
|
97
|
+
margin: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.section__hint {
|
|
101
|
+
font-size: 0.8rem;
|
|
102
|
+
color: var(--muted);
|
|
103
|
+
margin: 0.25rem 0 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.toolbar {
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: 0.5rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.toolbar button {
|
|
113
|
+
display: inline-flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
justify-content: center;
|
|
116
|
+
width: 2.5rem;
|
|
117
|
+
height: 2.5rem;
|
|
118
|
+
border: 1px solid rgba(28, 25, 23, 0.12);
|
|
119
|
+
border-radius: 10px;
|
|
120
|
+
background: var(--surface);
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
color: var(--text);
|
|
123
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
124
|
+
transition: background 0.15s, box-shadow 0.15s, transform 0.15s;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.toolbar button:hover:not(:disabled) {
|
|
128
|
+
background: #fafaf9;
|
|
129
|
+
box-shadow: var(--shadow);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.toolbar button:active:not(:disabled) {
|
|
133
|
+
transform: scale(0.96);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.toolbar button:disabled {
|
|
137
|
+
opacity: 0.35;
|
|
138
|
+
cursor: not-allowed;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Scroll track: flex row; library adds overflow + drag */
|
|
142
|
+
.scroller {
|
|
143
|
+
display: flex;
|
|
144
|
+
gap: 1rem;
|
|
145
|
+
padding: 0.5rem 0.25rem 1rem;
|
|
146
|
+
scroll-padding-inline: 0.25rem;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.scroller-wrap {
|
|
150
|
+
position: relative;
|
|
151
|
+
border-radius: var(--radius);
|
|
152
|
+
background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(255, 255, 255, 0.72));
|
|
153
|
+
border: 1px solid rgba(28, 25, 23, 0.06);
|
|
154
|
+
box-shadow: var(--shadow);
|
|
155
|
+
padding: 0.75rem 0.5rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.product-card {
|
|
159
|
+
flex: 0 0 15rem;
|
|
160
|
+
min-width: 15rem;
|
|
161
|
+
background: var(--surface);
|
|
162
|
+
border-radius: var(--radius);
|
|
163
|
+
overflow: hidden;
|
|
164
|
+
border: 1px solid rgba(28, 25, 23, 0.06);
|
|
165
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
166
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.product-card:hover {
|
|
170
|
+
transform: translateY(-3px);
|
|
171
|
+
box-shadow: var(--shadow-hover);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.product-card__media {
|
|
175
|
+
position: relative;
|
|
176
|
+
aspect-ratio: 4 / 3;
|
|
177
|
+
background: linear-gradient(145deg, #e7e5e4, #d6d3d1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.product-card__media img {
|
|
181
|
+
width: 100%;
|
|
182
|
+
height: 100%;
|
|
183
|
+
object-fit: cover;
|
|
184
|
+
display: block;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.product-card__badge {
|
|
188
|
+
position: absolute;
|
|
189
|
+
top: 0.65rem;
|
|
190
|
+
left: 0.65rem;
|
|
191
|
+
font-size: 0.65rem;
|
|
192
|
+
font-weight: 600;
|
|
193
|
+
text-transform: uppercase;
|
|
194
|
+
letter-spacing: 0.06em;
|
|
195
|
+
color: #fff;
|
|
196
|
+
background: rgba(28, 25, 23, 0.72);
|
|
197
|
+
backdrop-filter: blur(6px);
|
|
198
|
+
padding: 0.25rem 0.5rem;
|
|
199
|
+
border-radius: 6px;
|
|
200
|
+
max-width: calc(100% - 1.3rem);
|
|
201
|
+
white-space: nowrap;
|
|
202
|
+
overflow: hidden;
|
|
203
|
+
text-overflow: ellipsis;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.product-card__body {
|
|
207
|
+
padding: 0.9rem 1rem 1.1rem;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.product-card__title {
|
|
211
|
+
margin: 0 0 0.35rem;
|
|
212
|
+
font-size: 0.9rem;
|
|
213
|
+
font-weight: 600;
|
|
214
|
+
line-height: 1.35;
|
|
215
|
+
display: -webkit-box;
|
|
216
|
+
-webkit-line-clamp: 2;
|
|
217
|
+
-webkit-box-orient: vertical;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
min-height: 2.4em;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.product-card__desc {
|
|
223
|
+
margin: 0 0 0.65rem;
|
|
224
|
+
font-size: 0.72rem;
|
|
225
|
+
line-height: 1.45;
|
|
226
|
+
color: var(--muted);
|
|
227
|
+
display: -webkit-box;
|
|
228
|
+
-webkit-line-clamp: 2;
|
|
229
|
+
-webkit-box-orient: vertical;
|
|
230
|
+
overflow: hidden;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.product-card__row {
|
|
234
|
+
display: flex;
|
|
235
|
+
align-items: baseline;
|
|
236
|
+
justify-content: space-between;
|
|
237
|
+
gap: 0.5rem;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.product-card__price {
|
|
241
|
+
font-size: 1.05rem;
|
|
242
|
+
font-weight: 700;
|
|
243
|
+
color: var(--accent);
|
|
244
|
+
letter-spacing: -0.02em;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.product-card__id {
|
|
248
|
+
font-size: 0.65rem;
|
|
249
|
+
color: #a8a29e;
|
|
250
|
+
font-variant-numeric: tabular-nums;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
#status {
|
|
254
|
+
margin-top: 1.5rem;
|
|
255
|
+
padding: 0.75rem 1rem;
|
|
256
|
+
border-radius: 10px;
|
|
257
|
+
font-size: 0.85rem;
|
|
258
|
+
background: rgba(255, 255, 255, 0.65);
|
|
259
|
+
border: 1px solid rgba(28, 25, 23, 0.08);
|
|
260
|
+
color: var(--muted);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
#status.is-error {
|
|
264
|
+
color: #b91c1c;
|
|
265
|
+
border-color: rgba(185, 28, 28, 0.25);
|
|
266
|
+
background: #fef2f2;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
#status.is-ok {
|
|
270
|
+
color: #0f766e;
|
|
271
|
+
border-color: rgba(13, 148, 136, 0.25);
|
|
272
|
+
background: #f0fdfa;
|
|
273
|
+
}
|
|
274
|
+
</style>
|
|
275
|
+
</head>
|
|
276
|
+
<body>
|
|
277
|
+
<div class="page">
|
|
278
|
+
<header class="hero">
|
|
279
|
+
<span class="hero__eyebrow">Example</span>
|
|
280
|
+
<h1>shopify-drag-carousel</h1>
|
|
281
|
+
<p>
|
|
282
|
+
Products load from
|
|
283
|
+
<code>GET https://api.escuelajs.co/api/v1/products</code>
|
|
284
|
+
— drag the row or use arrows. This file ships in
|
|
285
|
+
<code>examples/demo.html</code> for developers.
|
|
286
|
+
</p>
|
|
287
|
+
</header>
|
|
288
|
+
|
|
289
|
+
<section class="section" aria-labelledby="sec-manual">
|
|
290
|
+
<div class="section__head">
|
|
291
|
+
<div>
|
|
292
|
+
<h2 class="section__title" id="sec-manual">Manual init + prev / next</h2>
|
|
293
|
+
<p class="section__hint">
|
|
294
|
+
<code>new DragCarousel('#strip-main', { buttons: { … } })</code>
|
|
295
|
+
</p>
|
|
296
|
+
</div>
|
|
297
|
+
<div class="toolbar">
|
|
298
|
+
<button type="button" id="btn-prev" aria-label="Previous products">←</button>
|
|
299
|
+
<button type="button" id="btn-next" aria-label="Next products">→</button>
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
<div class="scroller-wrap">
|
|
303
|
+
<div id="strip-main" class="scroller"></div>
|
|
304
|
+
</div>
|
|
305
|
+
</section>
|
|
306
|
+
|
|
307
|
+
<section class="section" aria-labelledby="sec-auto">
|
|
308
|
+
<div class="section__head">
|
|
309
|
+
<div>
|
|
310
|
+
<h2 class="section__title" id="sec-auto">Auto-init</h2>
|
|
311
|
+
<p class="section__hint">
|
|
312
|
+
Class <code>.drag-carousel</code> — initialized on
|
|
313
|
+
<code>DOMContentLoaded</code>
|
|
314
|
+
</p>
|
|
315
|
+
</div>
|
|
316
|
+
</div>
|
|
317
|
+
<div class="scroller-wrap">
|
|
318
|
+
<div id="strip-auto" class="drag-carousel scroller"></div>
|
|
319
|
+
</div>
|
|
320
|
+
</section>
|
|
321
|
+
|
|
322
|
+
<p id="status" role="status"></p>
|
|
323
|
+
</div>
|
|
324
|
+
|
|
325
|
+
<script src="../index.js"></script>
|
|
326
|
+
<script>
|
|
327
|
+
const API = "https://api.escuelajs.co/api/v1/products";
|
|
328
|
+
|
|
329
|
+
function truncate(s, n) {
|
|
330
|
+
const t = String(s || "");
|
|
331
|
+
return t.length <= n ? t : t.slice(0, n - 1) + "…";
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function productCard(p) {
|
|
335
|
+
const el = document.createElement("article");
|
|
336
|
+
el.className = "product-card";
|
|
337
|
+
|
|
338
|
+
const media = document.createElement("div");
|
|
339
|
+
media.className = "product-card__media";
|
|
340
|
+
|
|
341
|
+
const img = document.createElement("img");
|
|
342
|
+
img.loading = "lazy";
|
|
343
|
+
const src =
|
|
344
|
+
Array.isArray(p.images) && p.images.length
|
|
345
|
+
? p.images[0]
|
|
346
|
+
: "";
|
|
347
|
+
img.src =
|
|
348
|
+
src ||
|
|
349
|
+
"data:image/svg+xml," +
|
|
350
|
+
encodeURIComponent(
|
|
351
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"><rect fill="#d6d3d1" width="100%" height="100%"/></svg>'
|
|
352
|
+
);
|
|
353
|
+
const title = String(p.title || "");
|
|
354
|
+
img.alt = title;
|
|
355
|
+
|
|
356
|
+
const badge = document.createElement("span");
|
|
357
|
+
badge.className = "product-card__badge";
|
|
358
|
+
badge.textContent = p.category && p.category.name ? String(p.category.name) : "Product";
|
|
359
|
+
|
|
360
|
+
media.append(img, badge);
|
|
361
|
+
|
|
362
|
+
const body = document.createElement("div");
|
|
363
|
+
body.className = "product-card__body";
|
|
364
|
+
|
|
365
|
+
const h = document.createElement("h3");
|
|
366
|
+
h.className = "product-card__title";
|
|
367
|
+
h.textContent = title;
|
|
368
|
+
|
|
369
|
+
const desc = document.createElement("p");
|
|
370
|
+
desc.className = "product-card__desc";
|
|
371
|
+
desc.textContent = truncate(p.description || "", 120);
|
|
372
|
+
|
|
373
|
+
const row = document.createElement("div");
|
|
374
|
+
row.className = "product-card__row";
|
|
375
|
+
|
|
376
|
+
const price = document.createElement("span");
|
|
377
|
+
price.className = "product-card__price";
|
|
378
|
+
const n = Number(p.price);
|
|
379
|
+
price.textContent = Number.isFinite(n) ? "$" + n.toFixed(2) : "";
|
|
380
|
+
|
|
381
|
+
const id = document.createElement("span");
|
|
382
|
+
id.className = "product-card__id";
|
|
383
|
+
id.textContent = "#" + String(p.id != null ? p.id : "");
|
|
384
|
+
|
|
385
|
+
row.append(price, id);
|
|
386
|
+
body.append(h, desc, row);
|
|
387
|
+
el.append(media, body);
|
|
388
|
+
return el;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function fill(el, list) {
|
|
392
|
+
el.replaceChildren();
|
|
393
|
+
list.forEach((p) => el.appendChild(productCard(p)));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
(async () => {
|
|
397
|
+
const status = document.getElementById("status");
|
|
398
|
+
|
|
399
|
+
if (typeof DragCarousel === "undefined") {
|
|
400
|
+
status.className = "is-error";
|
|
401
|
+
status.textContent = "DragCarousel not found. Check script path to ../index.js";
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
status.textContent = "Loading products…";
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
const res = await fetch(API);
|
|
409
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
410
|
+
const all = await res.json();
|
|
411
|
+
if (!Array.isArray(all)) throw new Error("Unexpected JSON");
|
|
412
|
+
|
|
413
|
+
const a = all.slice(0, 12);
|
|
414
|
+
const b = all.slice(12, 24);
|
|
415
|
+
|
|
416
|
+
fill(document.getElementById("strip-main"), a);
|
|
417
|
+
fill(document.getElementById("strip-auto"), b);
|
|
418
|
+
|
|
419
|
+
void new DragCarousel("#strip-main", {
|
|
420
|
+
speed: 1.5,
|
|
421
|
+
buttons: { prev: "#btn-prev", next: "#btn-next" },
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
status.className = "is-ok";
|
|
425
|
+
status.textContent =
|
|
426
|
+
"Loaded " +
|
|
427
|
+
all.length +
|
|
428
|
+
" products (showing " +
|
|
429
|
+
a.length +
|
|
430
|
+
" + " +
|
|
431
|
+
b.length +
|
|
432
|
+
"). Serve this folder from the repo root: npm run demo → /examples/demo.html";
|
|
433
|
+
} catch (e) {
|
|
434
|
+
status.className = "is-error";
|
|
435
|
+
status.textContent =
|
|
436
|
+
"Could not load the API. Use http:// (e.g. npm run demo), not file://. " +
|
|
437
|
+
(e && e.message ? "(" + e.message + ")" : "");
|
|
438
|
+
}
|
|
439
|
+
})();
|
|
440
|
+
</script>
|
|
441
|
+
</body>
|
|
442
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shopify-drag-carousel",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Lightweight drag-scroll for horizontal containers—one class, zero dependencies. Shopify & vanilla JS friendly.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"index.js",
|
|
8
8
|
"style.css",
|
|
9
|
-
"README.md"
|
|
9
|
+
"README.md",
|
|
10
|
+
"examples/demo.html",
|
|
11
|
+
"examples/demo-cdn.html"
|
|
10
12
|
],
|
|
11
13
|
"scripts": {
|
|
12
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"demo": "python3 -m http.server 5173"
|
|
13
16
|
},
|
|
14
17
|
"repository": {
|
|
15
18
|
"type": "git",
|