sliderpro-agentic-skills-etch 0.1.0 → 0.1.2
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/package.json
CHANGED
|
@@ -444,19 +444,28 @@ handler inside `ready()`: the slider mounted before your code ran, so it has alr
|
|
|
444
444
|
Before configuring an unfamiliar slider, check whether it is a premade template. If it is, use
|
|
445
445
|
that template's documented knobs rather than inventing new CSS.
|
|
446
446
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
|
451
|
-
|
|
|
452
|
-
| Slider
|
|
453
|
-
| Slider
|
|
454
|
-
| Slider
|
|
455
|
-
|
|
|
456
|
-
|
|
|
457
|
-
|
|
|
458
|
-
|
|
459
|
-
|
|
447
|
+
Every template names its Wrapper, so one class on the Wrapper identifies all nine. Check that
|
|
448
|
+
first; the second column is confirmation.
|
|
449
|
+
|
|
450
|
+
| Template | Wrapper class | Also present | Script | Guide |
|
|
451
|
+
| --- | --- | --- | --- | --- |
|
|
452
|
+
| Slider Zeon | `.slider-zeon-wrapper` | `.slider-zeon__bg-thumb`, `.main-slider-zeon__content` | no | `https://design-with-cracka.gitbook.io/etchsliderpro/premade-templates` |
|
|
453
|
+
| Slider Chronos | `.slider-chronos-wrapper` | `.slider-chronos__slide`, `.slider-chronos__year` | no | `https://design-with-cracka.gitbook.io/etchsliderpro/premade-templates` |
|
|
454
|
+
| Slider Flow | `.slider-flow-wrapper` | `.slider-flow__slide`, `.slider-flow__slide-bg-media` | no | `https://design-with-cracka.gitbook.io/etchsliderpro/premade-templates` |
|
|
455
|
+
| Slider Team | `.slider-team-wrapper` | `.slider-team__grid`, `.slider-team__sync` | no | `https://design-with-cracka.gitbook.io/etchsliderpro/premade-templates` |
|
|
456
|
+
| Slider Stack | `.slider-stack-wrapper` | `.slider-stack__card`, `.slider-stack__card-lift` | no | `https://design-with-cracka.gitbook.io/etchsliderpro/card-stack-templates` |
|
|
457
|
+
| Slider Marquee | `.slider-marquee-wrapper` | two rows, Infinite Scroll on, Edge Fade on the Wrapper | no | `https://design-with-cracka.gitbook.io/etchsliderpro/premade-templates` |
|
|
458
|
+
| Deck Featured | `.slider-wrapper-deck-featured` | `.deck-stack-featured`, `.deck-card-featured` | yes | `https://design-with-cracka.gitbook.io/etchsliderpro/card-stack-templates` |
|
|
459
|
+
| Deck Testimonial | `.slider-wrapper-deck-testimonial` | `.deck-stack-testimonial`, `.deck-card-testimonial` | yes | `https://design-with-cracka.gitbook.io/etchsliderpro/card-stack-templates` |
|
|
460
|
+
| Fall | `.slider-wrapper-fall` | `.fall-stack`, `.fall-card` | yes | `https://design-with-cracka.gitbook.io/etchsliderpro/card-stack-templates` |
|
|
461
|
+
|
|
462
|
+
Note the two naming schemes: the six slider templates suffix the name (`.slider-flow-wrapper`),
|
|
463
|
+
the three card stacks infix it (`.slider-wrapper-fall`).
|
|
464
|
+
|
|
465
|
+
Five of the nine sync elements outside the track, and they do not all use the same prop. Team and
|
|
466
|
+
Stack set `sliderSetup.syncCustomElement` on the Slider; the three card stacks set
|
|
467
|
+
`sliderlessSync.customElement` on the Wrapper. Zeon pairs sliders by `sliderSetup.sliderRole`
|
|
468
|
+
instead, and Chronos, Flow and Marquee sync nothing.
|
|
460
469
|
|
|
461
470
|
The three that carry a script expose named constants at the top of it. On the two Deck templates,
|
|
462
471
|
`MAX_TIERS` is the deepest ring the CSS defines and `NAV_RINGS` is how many rings stay clickable.
|
|
@@ -142,25 +142,106 @@ etch.blocks.create(json, parentId?, index?) // returns new id
|
|
|
142
142
|
etch.components.list() // [{ id, name }, ...]
|
|
143
143
|
etch.components.getJson(id) // .properties and .blocks
|
|
144
144
|
etch.styles.list() // [{ id, selector, type, collection, css }]
|
|
145
|
+
etch.styles.create(selector, css) // returns a STYLE ID; pass it to a class prop
|
|
146
|
+
etch.blocks.addClass(blockId, name) // plain elements only; throws on a component
|
|
147
|
+
etch.blocks.getJson(id).script // { code }, the JavaScript code block
|
|
145
148
|
await etch.saveAsync()
|
|
146
149
|
```
|
|
147
150
|
|
|
148
151
|
### Component instances are the unit of work
|
|
149
152
|
|
|
150
|
-
A Slider Pro build is a tree of `etch/component` blocks, each
|
|
151
|
-
|
|
153
|
+
A Slider Pro build is a tree of `etch/component` blocks, each naming a component and carrying a
|
|
154
|
+
flat `attributes` map of prop values. **Two different shapes describe the same tree, and mixing
|
|
155
|
+
them up is the first thing that goes wrong in a session.**
|
|
156
|
+
|
|
157
|
+
**The live shape**, which `etch.blocks.getTree()` returns and `etch.blocks.create()` accepts:
|
|
152
158
|
|
|
153
159
|
```json
|
|
154
160
|
{
|
|
155
|
-
"
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
+
"type": "etch/component",
|
|
162
|
+
"version": 1,
|
|
163
|
+
"context": {},
|
|
164
|
+
"children": [],
|
|
165
|
+
"componentId": 6455,
|
|
166
|
+
"attributes": { "wrapperHeight": "350px", "spaceBetweenSliders": "50px" }
|
|
161
167
|
}
|
|
162
168
|
```
|
|
163
169
|
|
|
170
|
+
`version`, `context` and `children` are required. Block ids are strings (`"ot2wnef"`), not numbers.
|
|
171
|
+
|
|
172
|
+
**The export shape**, which you only ever see inside a premade template `.json` file, uses
|
|
173
|
+
`blockName`, nests under `attrs`, and calls the component id `ref`:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{ "blockName": "etch/component", "attrs": { "ref": 6455, "attributes": {} }, "innerBlocks": [] }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Never pass the export shape to `create()`. It is rejected with
|
|
180
|
+
`Invalid block JSON: expected string, received undefined` at `path: ["type"]`. To put a template on
|
|
181
|
+
the page, hand its whole `gutenbergBlock` to `etch.blocks.pasteAsync()` instead.
|
|
182
|
+
|
|
183
|
+
### Children go in a slot, never directly on the component
|
|
184
|
+
|
|
185
|
+
A component's children must be wrapped in an `etch/slot-content` node naming the slot they fill.
|
|
186
|
+
Putting a Slider straight inside a Wrapper's `children` renders nothing.
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{ "type": "etch/slot-content", "version": 1, "context": {}, "children": [], "slotName": "Slides" }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
| Component | Slots |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| DWC Slider Wrapper | `Sliders_and_Controls` |
|
|
195
|
+
| DWC Slider | `Top__Controls`, `Slides`, `Bottom__Controls` |
|
|
196
|
+
| DWC Slide | `Content` |
|
|
197
|
+
| DWC Slider Pagination | `PaginationButtons` |
|
|
198
|
+
| DWC Slider Nav Button | `Nav_Btn_Content` |
|
|
199
|
+
| DWC Slider Progress, DWC Slider Play-Pause | none |
|
|
200
|
+
|
|
201
|
+
Controls are siblings of the Slider inside `Sliders_and_Controls`, or children of the Slider's own
|
|
202
|
+
`Top__Controls` / `Bottom__Controls`. Both work, because controls find their own slider.
|
|
203
|
+
|
|
204
|
+
### Classes: two different mechanisms
|
|
205
|
+
|
|
206
|
+
Adding a class to a plain element and adding one to a component are **not the same operation**, and
|
|
207
|
+
the component path is the one that matters here, because everything Slider Pro ships is a component.
|
|
208
|
+
|
|
209
|
+
**On a component**, `addClass` throws `Block "<id>" is not an HTML block.` Classes go through the
|
|
210
|
+
component's own class-typed prop, and the value is the **style id**, not the class name:
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
const styleId = etch.styles.create('.flow-demo', 'border-radius: 1rem;'); // returns an id
|
|
214
|
+
etch.blocks.setAttribute(wrapperId, 'customClass', styleId);
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Etch resolves the id to the selector name when it renders, so the page gets
|
|
218
|
+
`class="dwc-slider-wrapper flow-demo"`. The id never appears in the output. Store the id, not the
|
|
219
|
+
name, so the class survives being renamed in the Etch UI.
|
|
220
|
+
|
|
221
|
+
The class-typed props, one per component:
|
|
222
|
+
|
|
223
|
+
| Component | Prop |
|
|
224
|
+
| --- | --- |
|
|
225
|
+
| DWC Slider Wrapper | `customClass` |
|
|
226
|
+
| DWC Slider | `sliderClass` |
|
|
227
|
+
| DWC Slide, Progress, Play-Pause, Pagination | `class` |
|
|
228
|
+
| DWC Slider Nav Button | `buttonClass`, `buttonWrapperClass` |
|
|
229
|
+
|
|
230
|
+
**On a plain element**, use `addClass` with the bare class name, no dot and no id:
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
const id = etch.blocks.create(el('article', {}, []));
|
|
234
|
+
etch.blocks.addClass(id, 'fall-card');
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Passing a style id here does not resolve. It is treated as a literal name and sanitised, so
|
|
238
|
+
`30xsolr` silently becomes the class `xsolr`. Creating the element with `attributes: { class: '...' }`
|
|
239
|
+
works too and is fine when you are building a subtree in one call.
|
|
240
|
+
|
|
241
|
+
Either way the CSS rule itself is `etch.styles.create('.fall-card', '...')`, which is what the
|
|
242
|
+
selector has to match.
|
|
243
|
+
|
|
244
|
+
|
|
164
245
|
### Group props and the one-extra-brace rule
|
|
165
246
|
|
|
166
247
|
Most Slider props live in groups (`layout`, `motion`, `slides`, `autoplay`, ...). A group is stored
|
|
@@ -305,9 +386,9 @@ unit. `props.pauseSlidersOnHover` on the Wrapper pauses both rows together.
|
|
|
305
386
|
|
|
306
387
|
Infinite Scroll needs the Auto-Scroll extension enabled in admin settings, which is the default.
|
|
307
388
|
|
|
308
|
-
### Synced elements (
|
|
389
|
+
### Synced elements (Team, Stack)
|
|
309
390
|
|
|
310
|
-
|
|
391
|
+
Driving arbitrary elements on the page from the slider is one prop.
|
|
311
392
|
|
|
312
393
|
```js
|
|
313
394
|
setGroup(sliderId, 'sliderSetup', { syncCustomElement: '.timeline-node',
|
|
@@ -319,8 +400,23 @@ moves, and with nav on, clicking one jumps the slider to it. **You write the CSS
|
|
|
319
400
|
states.** The elements do not need to be inside the slider and do not need to match the slide
|
|
320
401
|
count.
|
|
321
402
|
|
|
403
|
+
The selector takes a comma-separated list, so one slider can drive several groups at once. Team
|
|
404
|
+
uses `'.slider-team__sync, .slider-team__sync-heading'` to move a portrait and a heading together.
|
|
405
|
+
|
|
322
406
|
Grep `## 3. Sync Custom Element` in the reference for multiple selectors and the overlap caveat.
|
|
323
407
|
|
|
408
|
+
### Main and thumbnails in one wrapper (Zeon)
|
|
409
|
+
|
|
410
|
+
A full-bleed hero with a thumbnail strip needs no sync prop at all, just roles:
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
setGroup(mainId, 'sliderSetup', { sliderRole: 'main', transitionType: 'Fade' });
|
|
414
|
+
setGroup(thumbId, 'sliderSetup', { sliderRole: 'thumbnails', transitionType: 'Loop' });
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Both inside the same Wrapper and they pair automatically. A Wrapper can hold more than one
|
|
418
|
+
thumbnail slider, which is how Zeon runs a background layer and a strip off the same main.
|
|
419
|
+
|
|
324
420
|
### Carousel on mobile, grid on desktop
|
|
325
421
|
|
|
326
422
|
```js
|
|
@@ -345,25 +441,89 @@ setGroup(wrapperId, 'sliderlessSync', {
|
|
|
345
441
|
|
|
346
442
|
CSS handles the front card and its two neighbours from `is-active` / `is-prev` / `is-next`. What
|
|
347
443
|
CSS cannot do is know that a given card is *three* back, so the rows behind, the z-index ladder and
|
|
348
|
-
the click targets need a script. That is case 1 of the native-first gate.
|
|
349
|
-
|
|
444
|
+
the click targets need a script. That is case 1 of the native-first gate.
|
|
445
|
+
|
|
446
|
+
The contract between that script and the CSS is one attribute, **`data-pos`**, which the script
|
|
447
|
+
derives from `is-active` and writes on every card. It is a **signed** offset from the active card,
|
|
448
|
+
so the CSS can fan the left and right sides in opposite directions:
|
|
449
|
+
|
|
450
|
+
```css
|
|
451
|
+
.deck-stack-featured &:is([data-pos='2'], [data-pos='-2']) { /* ring 2, both sides */ }
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
The stack container carries **`data-tiers`**, how many rings are actually in play. It is computed,
|
|
455
|
+
not fixed: a looping deck needs spare cards to hide the wrap behind, so the script reduces the
|
|
456
|
+
tiers when there are too few cards.
|
|
457
|
+
|
|
458
|
+
Writing an unsigned rank, or naming the attribute anything else, produces a flat pile: the rules
|
|
459
|
+
above simply never match. The shipped script does this:
|
|
350
460
|
|
|
351
461
|
```js
|
|
352
462
|
// 1. make a custom property interpolable, so a blur can animate (case 2)
|
|
353
463
|
CSS.registerProperty({ name: '--stack-focus', syntax: '<number>',
|
|
354
464
|
inherits: true, initialValue: '0' });
|
|
355
465
|
|
|
356
|
-
// 2.
|
|
357
|
-
var
|
|
358
|
-
|
|
466
|
+
// 2. how many rings this deck can afford, given looping and the card count
|
|
467
|
+
var mayLoop = wrap.dataset.loop === 'true' && total >= MIN_LOOP;
|
|
468
|
+
stack.dataset.tiers = mayLoop ? Math.min(MAX_TIERS, Math.floor((total - 3) / 2)) : MAX_TIERS;
|
|
359
469
|
|
|
360
|
-
// 3.
|
|
470
|
+
// 3. per card: SIGNED offset from the active card. On a looping deck the offset
|
|
471
|
+
// wraps, so the far end of the list reads as the near side, not as distance 8.
|
|
472
|
+
var d = n - active;
|
|
473
|
+
if (mayLoop) {
|
|
474
|
+
if (d > total / 2) d -= total;
|
|
475
|
+
if (d < -total / 2) d += total;
|
|
476
|
+
}
|
|
477
|
+
card.dataset.pos = d;
|
|
478
|
+
|
|
479
|
+
// 4. hit-testing: stack order by distance, and clicks off beyond the clickable rings
|
|
480
|
+
var rank = Math.abs(d);
|
|
361
481
|
card.style.zIndex = String(total - rank);
|
|
362
482
|
lift.style.pointerEvents = rank <= NAV_RINGS ? 'auto' : 'none';
|
|
363
483
|
```
|
|
364
484
|
|
|
365
|
-
|
|
366
|
-
|
|
485
|
+
Do not clamp `data-pos` to the tier count. Cards past the deepest ring should match no rule and
|
|
486
|
+
stay stacked at the back; clamping piles them onto the last visible ring instead.
|
|
487
|
+
|
|
488
|
+
Both decks also set `--i` on each line of card content so the CSS can stagger it:
|
|
489
|
+
|
|
490
|
+
```js
|
|
491
|
+
card.querySelectorAll('.deck-card-featured__content > *')
|
|
492
|
+
.forEach(function (line, j) { line.style.setProperty('--i', j); });
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Step 4 is Deck only. **Fall's script is much smaller**: it sets `--i` and a signed `data-pos`, and
|
|
496
|
+
nothing else. Fall never loops, so there is no wrap to fold and no z-index ladder to maintain,
|
|
497
|
+
because the cards fall past each other rather than fanning around a front card.
|
|
498
|
+
|
|
499
|
+
Recompute on every change: watch the stack with a `MutationObserver` filtered to `class`, since
|
|
500
|
+
`is-active` moving is the only signal you get.
|
|
501
|
+
|
|
502
|
+
### Where a script goes
|
|
503
|
+
|
|
504
|
+
**Not in a `<script>` element.** Etch silently drops one from the render, so the page looks right
|
|
505
|
+
in the builder and ships with no behaviour at all.
|
|
506
|
+
|
|
507
|
+
Every block has an optional `script` field (`EtchBlockScript`), which is Etch's JavaScript code
|
|
508
|
+
block. Put the code on the **component instance that owns the markup**, which for a card stack is
|
|
509
|
+
the Wrapper:
|
|
510
|
+
|
|
511
|
+
```js
|
|
512
|
+
const wrapperId = etch.blocks.create({
|
|
513
|
+
type: 'etch/component', version: 1, context: {}, children: [ /* ... */ ],
|
|
514
|
+
componentId: C['DWC Slider Wrapper'], attributes: {},
|
|
515
|
+
script: { code: "(function () { /* ... */ })();" }
|
|
516
|
+
});
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
`code` is **plain source** over the API. The base64 you see in a premade template `.json` is only
|
|
520
|
+
how the export serialises it; do not encode it yourself.
|
|
521
|
+
|
|
522
|
+
Etch renders it as `<script type="module" defer>`, so it runs after parsing and top-level `await`
|
|
523
|
+
is available. Read it back with `etch.blocks.getJson(id).script`.
|
|
524
|
+
|
|
525
|
+
Component scripts are also the reason a card deck keeps working: the bridge leaves Sync Without
|
|
526
|
+
Slider wrappers unreconstructed precisely so an author's script keeps observing the original nodes.
|
|
367
527
|
|
|
368
528
|
Card counts for a looping deck: three are always visible (front plus two), and a loop needs a
|
|
369
529
|
hidden slot at each end, so **five is the minimum** and nine gives the full three-row fan. Fall
|
|
@@ -392,19 +552,30 @@ function setGroup(id, key, patch) {
|
|
|
392
552
|
etch.blocks.setAttribute(id, key, '{' + JSON.stringify(next) + '}');
|
|
393
553
|
}
|
|
394
554
|
|
|
395
|
-
function
|
|
396
|
-
return {
|
|
397
|
-
blockName: 'etch/component',
|
|
398
|
-
attrs: { ref: ref, attributes: attributes || {} },
|
|
399
|
-
innerBlocks: children || []
|
|
400
|
-
};
|
|
555
|
+
function node(extra, children) {
|
|
556
|
+
return Object.assign({ version: 1, context: {}, children: children || [] }, extra);
|
|
401
557
|
}
|
|
402
558
|
|
|
403
|
-
function
|
|
559
|
+
function component(componentId, attributes, children) {
|
|
560
|
+
return node({ type: 'etch/component', componentId: componentId,
|
|
561
|
+
attributes: attributes || {} }, children);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function slot(slotName, children) {
|
|
565
|
+
return node({ type: 'etch/slot-content', slotName: slotName }, children);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function el(tag, attributes, children) {
|
|
569
|
+
return node({ type: 'etch/element', tag: tag, attributes: attributes || {} }, children);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function text(t) { return node({ type: 'etch/text', text: t }); }
|
|
573
|
+
|
|
574
|
+
function findByRef(nodes, componentId, out) {
|
|
404
575
|
out = out || [];
|
|
405
576
|
for (const n of nodes || []) {
|
|
406
|
-
if (n.componentId ===
|
|
407
|
-
findByRef(n.children
|
|
577
|
+
if (n.componentId === componentId) out.push(n);
|
|
578
|
+
findByRef(n.children, componentId, out);
|
|
408
579
|
}
|
|
409
580
|
return out;
|
|
410
581
|
}
|
|
@@ -415,18 +586,27 @@ function findByRef(nodes, ref, out) {
|
|
|
415
586
|
```js
|
|
416
587
|
const C = comps();
|
|
417
588
|
const slides = [];
|
|
418
|
-
for (let i = 0; i < 5; i++)
|
|
589
|
+
for (let i = 0; i < 5; i++) {
|
|
590
|
+
slides.push(component(C['DWC Slide'], {}, [
|
|
591
|
+
slot('Content', [ el('div', {}, [ text('Slide ' + (i + 1)) ]) ])
|
|
592
|
+
]));
|
|
593
|
+
}
|
|
419
594
|
|
|
420
595
|
const id = etch.blocks.create(
|
|
421
596
|
component(C['DWC Slider Wrapper'], {}, [
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
597
|
+
slot('Sliders_and_Controls', [
|
|
598
|
+
component(C['DWC Slider'], {}, [ slot('Slides', slides) ]),
|
|
599
|
+
component(C['DWC Slider Nav Button'], {}),
|
|
600
|
+
component(C['DWC Slider Pagination'], {})
|
|
601
|
+
])
|
|
425
602
|
])
|
|
426
603
|
);
|
|
427
604
|
return id;
|
|
428
605
|
```
|
|
429
606
|
|
|
607
|
+
An empty Slide has no content and so no height. Give slides something to size before wondering why
|
|
608
|
+
the slider is a flat line.
|
|
609
|
+
|
|
430
610
|
Build **one** first, screenshot it, then scale to the full count. Do not generate twenty slides
|
|
431
611
|
before you have seen one render.
|
|
432
612
|
|