Skip to content

Commit

Permalink
Editions.dev workshop start state
Browse files Browse the repository at this point in the history
  • Loading branch information
skparkk committed Jun 24, 2024
1 parent 80bc484 commit a279432
Show file tree
Hide file tree
Showing 44 changed files with 554 additions and 5,674 deletions.
47 changes: 42 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ Theme blocks are a new feature for online stores that allow theme developers to

### Benefits of theme blocks

* Less duplicate code
* More consistent merchant experience
* Theme blocks can be nested
* More flexible sections
- Less duplicate code
- More consistent merchant experience
- Theme blocks can be nested
- More flexible sections

## Creating theme blocks

Expand Down Expand Up @@ -50,6 +50,7 @@ For more details, see the [theme block documentation](https://shopify.dev/docs/t
}
{% endschema %}
```

</details>

Note that a preset is required for a theme block to appear in the editor, the same as for sections.
Expand All @@ -64,7 +65,7 @@ Note that theme blocks and local blocks cannot be used in the same section.

## Referencing theme blocks in JSON templates

To include theme blocks in your JSON templates, add a section that accepts them in the editor and then click "add block" in the section sidebar. You will see a list of theme blocks and can add them to the section.
To include theme blocks in your JSON templates, add a section that accepts them in the editor and then click "add block" in the section sidebar. You will see a list of theme blocks and can add them to the section.

You can also do this directly in the JSON file. You must include a `"blocks"` key and a `"block_order"` key in your section.

Expand Down Expand Up @@ -95,6 +96,7 @@ You can also do this directly in the JSON file. You must include a `"blocks"` ke
]
}
```

</details>

## Nesting blocks
Expand All @@ -112,3 +114,38 @@ The theme editor now includes drag and drop interactions in the power preview fo
Like sections, blocks can have presets. This allows blocks to be used in more flexible ways.

A block must have at least one preset to appear for selection in the Theme Editor.

## Theme block targeting

A section or group that accepts theme blocks can indicate either that it accepts all blocks (and apps) by adding `"blocks": [{ "type": "@theme" }, { "type": "@app" }]` to the schema. Or it can target specific blocks by adding `"blocks": [{ "type": "slide" }]`

## Static theme blocks

Theme blocks can be referenced directly in Liquid using `content_for` as follows:

```
{% content_for "block", type: "slideshow-controls", id: "slideshow-controls" %}
```

Settings for these blocks can be configured by the merchant via the theme editor. They can also be configured by the theme developer in presets. Their usage in presets is the same as a normal theme block except it includes `"static": true` and requires a matching `id` to the one in the corresponding Liquid.

## Styles

Style settings can now be added to sections and blocks. These are new input setting types that offer CSS styles for merchants to customize.

Currently the categories that exist are:

- `"type": "style.size_panel"` - This panel includes CSS properties like `width`, `height`, and `flex-grow`.
- `"type": "style.spacing_panel"` - This panel includes CSS properties like `flex-direction`, `justify-content`, and `align items`.
- `"type": "style.layout_panel"` - This panel includes CSS properties like `padding` and `margin`.

These settings are applied via the new filter `class_list`, which renders the set of classes that correspond to the used style settings within a section or block. It is used as follows: `<div class="{{ block.settings | class_list }}">`. In a section, `section.settings` is used instead.

You can also apply different classes to different elements, for example:

```
<div class="{{ block.settings.size | class_list }}">...</div>
<div class="{{ block.settings.layout | class_list }}">...</div>
```

As with a normal setting, it is accessed via its ID.
64 changes: 14 additions & 50 deletions assets/base.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
:root {
--layout-gap: 15px;
--page-width: 1200px;
}

* {
Expand Down Expand Up @@ -123,27 +122,6 @@ p {
padding: 0;
}

.placeholder-image {
position: relative;
width: 100%;
aspect-ratio: 1/1;
overflow: hidden;
}

.placeholder-image svg {
position: absolute;
width: 100%;
height: 100%;
}

.page-width {
max-width: var(--page-width);
width: 100%;
margin-inline: 1rem;
padding-inline: 1.5rem;
box-sizing: border-box;
}

.text-center {
text-align: center;
}
Expand All @@ -152,24 +130,6 @@ p {
text-align: end;
}

@media screen and (min-width: 750px) {
.page-width {
margin-inline: 5rem;
padding-inline: 5rem;
}
}

@media screen and (min-width: 1299px) {
.page-width {
margin-inline: auto;
}
}

.page-width--full {
max-width: 100%;
margin-inline: 0;
}

/* Section & block group */
@media only screen and (max-width: 749px) {
.layout > *:not(.link-block, .icon-block) {
Expand All @@ -194,16 +154,6 @@ p {
gap: var(--layout-gap);
}

.section-content-wrapper {
position: relative;
display: flex;
flex-wrap: wrap;
width: 100%;
max-width: var(--page-width);
margin-inline: auto;
gap: var(--layout-gap);
}

.section--group {
background-repeat: no-repeat;
background-size: cover;
Expand Down Expand Up @@ -292,6 +242,20 @@ button, .button {
.image-block img {
height: 100%;
object-fit: cover;
aspect-ratio: 1 / 1;
}

.placeholder-image {
position: relative;
width: 100%;
aspect-ratio: 1/1;
overflow: hidden;
}

.placeholder-image svg {
position: absolute;
width: 100%;
height: 100%;
}

/* Text block */
Expand Down
174 changes: 174 additions & 0 deletions assets/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,177 @@ class QuantityInput extends HTMLElement {
}

customElements.define('quantity-input', QuantityInput);

// Slideshow
class SlideshowComponent extends HTMLElement {
constructor() {
super();
this.slides = this.querySelectorAll(".slideshow__slide");
if ( this.slides.length <= 1) return;

this.currentSlideIndicator = this.querySelector(".current-slide-indicator");
this.currentSlideIndex = this.dataset.activeSlide;
this.counterControls = this.querySelectorAll(".slideshow-controls__link");

this.nextControl = this.querySelector(".slideshow-control--next");
this.previousControl = this.querySelector(".slideshow-control--previous");

if (this.getAttribute('data-autoplay') === 'true') {
this.autoplaySpeed = parseInt(this.dataset.autoplaySpeed) * 1000;
this.isAutoplayButtonSetToPlay = true;
this.setAutoPlay();
}

this.initButtons();
}

initButtons() {
this.nextControl.addEventListener("click", () => {
this.updateSlideshow('next');
this.resetAutoPlay();
});

this.previousControl.addEventListener("click", () => {
this.updateSlideshow('previous');
this.resetAutoPlay();
});

this.counterControls.forEach(control => {
control.addEventListener("click", () => {
if (this.currentSlideIndex == control.dataset.slideNumber) return;
this.updateSlideshow(control.dataset.slideNumber);
this.resetAutoPlay();
});
});
}

updateSlideshow(target = 'next') {
let nextSlideIndex = 0;

switch (target) {
case 'next':
nextSlideIndex = parseInt(this.dataset.activeSlide) + 1;
break;
case 'previous':
nextSlideIndex = parseInt(this.dataset.activeSlide) - 1;
break;
default:
nextSlideIndex = target;
break;
}

const transitions = ['left', 'top', 'right', 'bottom'];
const transitionStyle = nextSlideIndex > this.dataset.activeSlide ? this.dataset.transitionStyle : transitions[(transitions.indexOf(this.dataset.transitionStyle)+2) % transitions.length];

if (nextSlideIndex < 0) {
nextSlideIndex = this.slides.length - 1;
}
else if (nextSlideIndex >= this.slides.length) {
nextSlideIndex = 0;
}

this.slideTransition(this.slides[this.dataset.activeSlide], this.slides[nextSlideIndex], transitionStyle);
this.updateControls(nextSlideIndex);
this.dataset.activeSlide = nextSlideIndex;
this.currentSlideIndex = nextSlideIndex;
}

slideTransition(currentSlide, nextSlide, transitionStyle = 'left') {
const slideshowControls = this.querySelectorAll('.slideshow-control');
slideshowControls.forEach((control) => { control.setAttribute('disabled', 'true')});

currentSlide.addEventListener('transitionend', (event) => {
// Make sure we're only looking at the property that interest us
if (event.propertyName !== 'transform') return;

// Hide slides & reset transitions
this.slides.forEach((slide) => {
slide.setAttribute('aria-hidden', 'true');
slide.classList.remove(`slide--transition-${transitionStyle}`);
});

// Set new slide to active
nextSlide.setAttribute('aria-hidden', 'false');
nextSlide.classList.remove('slide--next');
slideshowControls.forEach(control => { control.removeAttribute('disabled')});
});

// Start slide transition
currentSlide.classList.add(`slide--transition-${transitionStyle}`);
nextSlide.classList.add('slide--next');
}

updateControls(newSlideIndex) {
// If we have a current slide indicator (i.e. 2/3), update it
if (this.currentSlideIndicator) {
this.currentSlideIndicator.innerText = `${newSlideIndex + 1}/${this.slides.length}`;
return;
}

this.counterControls.forEach(control => { control.classList.remove("slideshow-controls__link--active")});
this.counterControls[newSlideIndex].classList.add("slideshow-controls__link--active");
}

resetAutoPlay() {
if (this.isAutoplayButtonSetToPlay) {
this.play();
}
}

/* Handle autoplay */
setAutoPlay() {
this.addEventListener('mouseover', this.focusInHandling.bind(this));
this.addEventListener('mouseleave', this.focusOutHandling.bind(this));
this.addEventListener('focusin', this.focusInHandling.bind(this));
this.addEventListener('focusout', this.focusOutHandling.bind(this));

this.sliderAutoplayButton = this.querySelector('.slideshow-control--autoplay');
this.sliderAutoplayButton.addEventListener('click', this.autoPlayToggle.bind(this));
this.isAutoplayButtonSetToPlay = true;
this.play();
}

autoPlayToggle() {
this.togglePlayButtonState(this.isAutoplayButtonSetToPlay);
this.isAutoplayButtonSetToPlay ? this.pause() : this.play();
this.isAutoplayButtonSetToPlay = !this.isAutoplayButtonSetToPlay;
}

togglePlayButtonState(pauseAutoplay) {
this.sliderAutoplayButton.classList.toggle('slideshow-control--autoplay--paused', pauseAutoplay);
(pauseAutoplay) ? this.sliderAutoplayButton.setAttribute('aria-label', 'Play slideshow') : this.sliderAutoplayButton.setAttribute('aria-label', 'Pause slideshow');
}

play() {
this.setAttribute('aria-live', 'off');
clearInterval(this.autoplay);
this.autoplay = setInterval(this.autoRotateSlides.bind(this), this.autoplaySpeed);
}

pause() {
this.setAttribute('aria-live', 'polite');
clearInterval(this.autoplay);
}

autoRotateSlides() {
this.updateSlideshow('next');
}

focusOutHandling(event) {
const focusedOnAutoplayButton =
event.target === this.sliderAutoplayButton || this.sliderAutoplayButton.contains(event.target);

if (this.isAutoplayButtonSetToPlay && !focusedOnAutoplayButton) this.play();
}

focusInHandling(event) {
const focusedOnAutoplayButton =
event.target === this.sliderAutoplayButton || this.sliderAutoplayButton.contains(event.target);

if (this.isAutoplayButtonSetToPlay) {
focusedOnAutoplayButton ? this.play() : this.pause();
}
}
};

customElements.define('slideshow-component', SlideshowComponent);
Loading

0 comments on commit a279432

Please sign in to comment.