@gemboss/gem-framework 0.18.16 → 0.19.0
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 +80 -1
- package/dist/SOURCE.json +3 -3
- package/dist/gem-framework.css +23 -1
- package/dist/gem-runtime.js +94 -54
- package/dist/index.js +1668 -43
- package/dist/motion/core.d.ts +208 -0
- package/dist/motion/core.js +1 -0
- package/dist/motion/orbit.d.ts +4 -0
- package/dist/motion/orbit.js +1 -0
- package/dist/motion/quick-view.d.ts +4 -0
- package/dist/motion/quick-view.js +1 -0
- package/dist/motion/rail.d.ts +5 -0
- package/dist/motion/rail.js +1 -0
- package/dist/orbit-base.css +39 -0
- package/dist/plugins/before_after.js +1 -1
- package/dist/plugins/bundle.js +1 -1
- package/dist/plugins/carousel.js +1 -1
- package/dist/plugins/cart-drawer.js +1 -1
- package/dist/plugins/context-adapter.js +1 -1
- package/dist/plugins/countdown.js +1 -1
- package/dist/plugins/disclosure.js +1 -1
- package/dist/plugins/hero-slideshow.js +1 -1
- package/dist/plugins/img-in.js +1 -1
- package/dist/plugins/localization.js +1 -1
- package/dist/plugins/media-gallery.js +1 -1
- package/dist/plugins/mobile-menu.js +1 -1
- package/dist/plugins/offer-engine.js +1 -1
- package/dist/plugins/orbit.d.ts +9 -0
- package/dist/plugins/orbit.js +1 -0
- package/dist/plugins/predictive-search.js +1 -1
- package/dist/plugins/promo-popup.js +1 -1
- package/dist/plugins/quantity-input.js +1 -1
- package/dist/plugins/quick-view.js +1 -69
- package/dist/plugins/rail.d.ts +9 -0
- package/dist/plugins/rail.js +1 -0
- package/dist/plugins/recently-viewed.js +1 -1
- package/dist/plugins/reveal-on-scroll.js +1 -1
- package/dist/plugins/reviews.js +1 -1
- package/dist/plugins/runtime-slot-loader.js +1 -1
- package/dist/plugins/seasonal-effects.js +1 -1
- package/dist/plugins/size-guide.d.ts +9 -0
- package/dist/plugins/size-guide.js +38 -0
- package/dist/plugins/sticky-atc.js +1 -1
- package/dist/plugins/tabs.js +1 -1
- package/dist/plugins/variant-selects.js +1 -1
- package/dist/popup-shell.css +15 -0
- package/dist/primitives.css +4 -1
- package/dist/rail-base.css +48 -0
- package/dist/runtime-core.d.ts +2 -0
- package/dist/runtime-core.js +26 -15
- package/dist/section-gate.js +2368 -36
- package/dist/tokens.css +4 -0
- package/package.json +11 -1
|
@@ -1,69 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// packages/gem-framework/src/runtime.ts, the same lines gem-runtime.js carries; do not edit here.
|
|
3
|
-
import { mountPlugin } from "../runtime-core.js";
|
|
4
|
-
|
|
5
|
-
export const key = "quick-view";
|
|
6
|
-
export const readyEvent = "gem:quick-view:ready";
|
|
7
|
-
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
9
|
-
registerPlugin('quick-view', ({ api }) => {
|
|
10
|
-
const triggers = document.querySelectorAll('[data-gem-quick-view], [data-gem-action="quick-view"]');
|
|
11
|
-
const surface = document.querySelector('[data-gem-quick-view-surface], .gem-quick-view, .gem-modal[data-gem-component="quick-view"]');
|
|
12
|
-
let bound = 0;
|
|
13
|
-
const close = (reason = 'close-control') => {
|
|
14
|
-
if (surface) {
|
|
15
|
-
surface.dataset.gemQuickViewState = 'closed';
|
|
16
|
-
surface.removeAttribute && surface.removeAttribute('open');
|
|
17
|
-
surface.setAttribute && surface.setAttribute('aria-hidden', 'true');
|
|
18
|
-
}
|
|
19
|
-
api.emit('gem:quick-view:close', { source: 'gem-runtime', reason });
|
|
20
|
-
};
|
|
21
|
-
const openSurface = (detail) => {
|
|
22
|
-
if (surface) {
|
|
23
|
-
surface.dataset.gemQuickViewState = 'open';
|
|
24
|
-
surface.dataset.gemProductHandle = detail.productHandle || '';
|
|
25
|
-
surface.dataset.gemProductUrl = detail.productUrl || '';
|
|
26
|
-
surface.setAttribute && surface.setAttribute('open', '');
|
|
27
|
-
surface.setAttribute && surface.setAttribute('aria-hidden', 'false');
|
|
28
|
-
surface.setAttribute && surface.setAttribute('role', 'dialog');
|
|
29
|
-
surface.setAttribute && surface.setAttribute('aria-modal', 'true');
|
|
30
|
-
const content = surface.querySelector && surface.querySelector('[data-gem-quick-view-content]');
|
|
31
|
-
if (content && content.textContent !== undefined) content.textContent = 'Quick view for ' + (detail.productHandle || 'product');
|
|
32
|
-
else if (surface.textContent !== undefined) surface.textContent = 'Quick view for ' + (detail.productHandle || 'product');
|
|
33
|
-
const closeButton = surface.querySelector && surface.querySelector('[data-gem-quick-view-close]');
|
|
34
|
-
if (closeButton && closeButton.focus) closeButton.focus();
|
|
35
|
-
}
|
|
36
|
-
api.emit('gem:quick-view:open', { source: 'gem-runtime', ...detail, hasSurface: Boolean(surface) });
|
|
37
|
-
};
|
|
38
|
-
triggers.forEach((trigger) => {
|
|
39
|
-
if (!mark(trigger, 'quick-view')) return;
|
|
40
|
-
if (!canListen(trigger)) return;
|
|
41
|
-
__gemListen(trigger, 'click', (event) => {
|
|
42
|
-
const productUrl = trigger.getAttribute && (trigger.getAttribute('data-gem-product-url') || trigger.getAttribute('href'));
|
|
43
|
-
const productHandle = trigger.getAttribute && trigger.getAttribute('data-gem-product-handle');
|
|
44
|
-
if (!productUrl && !productHandle) return;
|
|
45
|
-
event.preventDefault && event.preventDefault();
|
|
46
|
-
openSurface({ productUrl, productHandle });
|
|
47
|
-
});
|
|
48
|
-
bound += 1;
|
|
49
|
-
});
|
|
50
|
-
if (surface && mark(surface, 'quick-view-surface')) {
|
|
51
|
-
surface.dataset.gemQuickViewState = 'closed';
|
|
52
|
-
surface.setAttribute && surface.setAttribute('aria-hidden', 'true');
|
|
53
|
-
if (canListen(surface)) __gemListen(surface, 'click', (event) => {
|
|
54
|
-
const target = event.target;
|
|
55
|
-
if (target && target.closest && target.closest('[data-gem-quick-view-close]')) close('close-control');
|
|
56
|
-
});
|
|
57
|
-
if (canListen(surface)) __gemListen(surface, 'keydown', (event) => { if (event.key === 'Escape') close('escape'); });
|
|
58
|
-
}
|
|
59
|
-
api.quickView = { open: (detail = {}) => openSurface({ ...detail }), close };
|
|
60
|
-
api.emit('gem:quick-view:ready', { source: 'gem-runtime', count: bound, hasSurface: Boolean(surface) });
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** Bind every "quick-view" hook inside root (default: the whole document). Returns unmount(). Mounting the same root twice binds once. */
|
|
65
|
-
export function mount(root, options) {
|
|
66
|
-
return mountPlugin(key, install, root, options);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export default mount;
|
|
1
|
+
import{mountPlugin as w}from"../runtime-core.js";import{mountEffects as b}from"../motion/core.js";import{quickViewEffect as h}from"../motion/quick-view.js";const _="quick-view",E="gem:quick-view:ready";function q({document:a,window:u,target:y,mark:m,canListen:c,listen:n,registerPlugin:v,registerNoopPlugin:A,onUnmount:f}){v("quick-view",({api:o})=>{const k=a.querySelectorAll('[data-gem-quick-view], [data-gem-action="quick-view"]'),e=a.querySelector('[data-gem-quick-view-surface], .gem-quick-view, .gem-modal[data-gem-component="quick-view"]');let l=0;const g=(t="close-control")=>{e&&(e.dataset.gemQuickViewState="closed",e.removeAttribute&&e.removeAttribute("open"),e.setAttribute&&e.setAttribute("aria-hidden","true")),o.emit("gem:quick-view:close",{source:"gem-runtime",reason:t})},d=t=>{if(e){e.dataset.gemQuickViewState="open",e.dataset.gemProductHandle=t.productHandle||"",e.dataset.gemProductUrl=t.productUrl||"",e.setAttribute&&e.setAttribute("open",""),e.setAttribute&&e.setAttribute("aria-hidden","false"),e.setAttribute&&e.setAttribute("role","dialog"),e.setAttribute&&e.setAttribute("aria-modal","true");const i=e.querySelector&&e.querySelector("[data-gem-quick-view-content]");i&&i.textContent!==void 0?i.textContent="Quick view for "+(t.productHandle||"product"):e.textContent!==void 0&&(e.textContent="Quick view for "+(t.productHandle||"product"));const r=e.querySelector&&e.querySelector("[data-gem-quick-view-close]");r&&r.focus&&r.focus()}o.emit("gem:quick-view:open",{source:"gem-runtime",...t,hasSurface:!!e})};k.forEach(t=>{m(t,"quick-view")&&c(t)&&(n(t,"click",i=>{const r=t.getAttribute&&(t.getAttribute("data-gem-product-url")||t.getAttribute("href")),s=t.getAttribute&&t.getAttribute("data-gem-product-handle");!r&&!s||(i.preventDefault&&i.preventDefault(),d({productUrl:r,productHandle:s}))}),l+=1)}),e&&m(e,"quick-view-surface")&&(e.dataset.gemQuickViewState="closed",e.setAttribute&&e.setAttribute("aria-hidden","true"),c(e)&&n(e,"click",t=>{const i=t.target;i&&i.closest&&i.closest("[data-gem-quick-view-close]")&&g("close-control")}),c(e)&&n(e,"keydown",t=>{t.key==="Escape"&&g("escape")})),(e||!o.quickView)&&(o.quickView={open:(t={})=>d({...t}),close:g});const p=b(h,{window:u,document:a,on:(t,i,r,s)=>{c(t)&&n(t,i,r,s)},mark:m,api:o,css:"[data-gem-quick-view-trigger]{--gem-quick-view-css:1}.gem-quick-view__shell{display:contents}.gem-quick-view__shell .gem-quick-view__dialog{overflow:visible;isolation:isolate;color:var(--gem-color-text);background:transparent;box-shadow:none;user-select:text;-webkit-user-select:text}.gem-quick-view__surface{position:absolute;inset:0;z-index:-1;border-radius:inherit;box-shadow:var(--gem-shadow-md);background:color-mix(in srgb,var(--gem-motion-tint,var(--gem-color-background)) 45%,var(--gem-color-background));transition:background-color var(--gem-motion-slow,0.5s) var(--gem-ease-soft,ease)}.gem-quick-view__shell .gem-quick-view__media{overflow:visible;background:transparent;min-height:0}.gem-quick-view__image{position:absolute;inset:6% 4%;width:92%;height:88%;object-fit:contain;transform-origin:50% 50%;will-change:transform}.gem-quick-view__image--next{opacity:0;pointer-events:none}.gem-quick-view__image--next:not([src]){display:none}.gem-quick-view__shell .gem-quick-view__panel{justify-content:flex-start;gap:var(--gem-space-sm)}.gem-quick-view__shell [hidden]{display:none}.gem-quick-view__bar{display:flex;align-items:center;gap:var(--gem-space-xs);font-family:var(--gem-font-mono);font-size:var(--gem-text-xs);font-variant-numeric:tabular-nums}.gem-quick-view__icon{display:inline-grid;place-items:center;width:var(--gem-tap-min);height:var(--gem-tap-min);padding:0;cursor:pointer;border:var(--gem-stroke,1px) solid color-mix(in srgb,currentColor 25%,transparent);border-radius:var(--gem-radius-pill);color:inherit;background:transparent;font:inherit;font-size:var(--gem-h5);line-height:1}.gem-quick-view__icon:hover{border-color:currentColor}.gem-quick-view__icon:focus-visible,.gem-quick-view__swatch:focus-visible,.gem-quick-view__size:focus-visible,.gem-quick-view__details:focus-visible{outline:var(--gem-focus-ring);outline-offset:var(--gem-focus-offset)}.gem-quick-view__eyebrow{margin:var(--gem-space-md) 0 0;color:var(--gem-color-muted);font-family:var(--gem-font-mono);font-size:var(--gem-text-xs);letter-spacing:0.12em;line-height:1.3;text-transform:uppercase}.gem-quick-view__title{margin:0;font-family:var(--gem-font-heading);font-size:var(--gem-h2);line-height:1.05}.gem-quick-view__price{margin:0;font-size:var(--gem-h5);font-variant-numeric:tabular-nums}.gem-quick-view__compare{margin:calc(-1 * var(--gem-space-xs)) 0 0;color:var(--gem-color-muted);font-size:var(--gem-text-sm)}.gem-quick-view__swatches,.gem-quick-view__sizes{display:flex;flex-wrap:wrap;gap:var(--gem-space-xs)}.gem-quick-view__swatch{position:relative;width:var(--gem-tap-min);height:var(--gem-tap-min);padding:0;border:0;color:inherit;background:transparent;cursor:pointer}.gem-quick-view__swatch::before{content:'';position:absolute;inset:8px;border-radius:var(--gem-radius-pill);background:var(--gem-quick-view-swatch,var(--gem-color-surface));box-shadow:inset 0 0 0 var(--gem-stroke,1px) color-mix(in srgb,var(--gem-color-text) 15%,transparent)}.gem-quick-view__swatch[aria-checked='true']::after{content:'';position:absolute;inset:3px;border:var(--gem-stroke-thick,2px) solid currentColor;border-radius:var(--gem-radius-pill)}.gem-quick-view__size{display:inline-grid;place-items:center;min-width:var(--gem-tap-min);height:var(--gem-tap-min);padding:0 var(--gem-space-sm);cursor:pointer;border:var(--gem-stroke,1px) solid color-mix(in srgb,currentColor 22%,transparent);border-radius:var(--gem-radius-pill);color:inherit;background:transparent;font:inherit;font-size:var(--gem-text-sm)}.gem-quick-view__size[aria-checked='true']{color:var(--gem-color-background);background:var(--gem-color-text);border-color:var(--gem-color-text)}.gem-quick-view__size:disabled{opacity:0.4;text-decoration:line-through;cursor:not-allowed}.gem-quick-view__add{width:100%;margin:var(--gem-space-xs) 0 0;font-variant-numeric:tabular-nums}.gem-quick-view__status{margin:0;min-height:1.2em;color:var(--gem-color-muted);font-size:var(--gem-text-sm)}.gem-quick-view__description{margin:0;max-width:46ch;font-size:var(--gem-text-sm);line-height:1.55}.gem-quick-view__details{color:inherit;font-size:var(--gem-text-sm);text-underline-offset:3px}[data-gem-quick-view-source]{visibility:hidden}@keyframes gem-quick-view-fade{from{opacity:0}to{opacity:1}}.gem-quick-view__dialog[data-gem-state='open']::backdrop{animation:gem-quick-view-fade var(--gem-motion-base,0.35s) ease both}.gem-quick-view__dialog[data-gem-state='closing']::backdrop{animation:gem-quick-view-fade var(--gem-motion-base,0.35s) ease reverse both}@media (max-width:749px){.gem-quick-view__shell .gem-quick-view__media{height:34dvh}}@media (prefers-reduced-motion:reduce){.gem-quick-view__dialog::backdrop{animation:none}.gem-quick-view__surface{transition:none}}"},f);o.emit("gem:quick-view:ready",{source:"gem-runtime",count:l+p,hasSurface:!!e})})}function x(a,u){return w(_,q,a,u)}var H=x;export{H as default,_ as key,x as mount,E as readyEvent};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @gemboss/gem-framework/plugins/rail: types of the ES module next to this file. Generated; do not edit here.
|
|
2
|
+
import type { MountOptions, Unmount } from "../runtime-core.js";
|
|
3
|
+
|
|
4
|
+
export type { MountOptions, Unmount };
|
|
5
|
+
export declare const key: "rail";
|
|
6
|
+
export declare const readyEvent: "gem:rail:ready";
|
|
7
|
+
/** Bind every "rail" hook inside root (default: the whole document). Returns unmount(). Mounting the same root twice binds once. */
|
|
8
|
+
export declare function mount(root?: ParentNode | null, options?: MountOptions): Unmount;
|
|
9
|
+
export default mount;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mountPlugin as d}from"../runtime-core.js";import{mountEffects as f}from"../motion/core.js";import{railEffect as p}from"../motion/rail.js";const _="rail",P="gem:rail:ready";function v({document:e,window:t,target:x,mark:i,canListen:o,listen:n,registerPlugin:m,registerNoopPlugin:y,onUnmount:s}){m("rail",({api:a})=>{const g=f(p,{window:t,document:e,on:(r,l,c,u)=>{o(r)&&n(r,l,c,u)},mark:i,api:a,css:".gem-rail{--gem-rail-css:1;user-select:none;-webkit-user-select:none;transition:background-color var(--gem-motion-slow,0.6s) var(--gem-ease-soft,ease)}.gem-rail[data-gem-state] .gem-rail__item{will-change:transform}.gem-rail[data-gem-state] .gem-rail__stage{cursor:pointer}.gem-rail[data-gem-state] .gem-rail__hit{pointer-events:none;transition:opacity var(--gem-motion-base,0.35s) var(--gem-ease-soft,ease)}@container (max-width:699px){.gem-rail[data-gem-state] .gem-rail__hit{pointer-events:auto}}.gem-rail[data-gem-state='open'] .gem-rail__hit{opacity:0.15}@media (prefers-reduced-motion:reduce){.gem-rail,.gem-rail[data-gem-state] .gem-rail__hit{transition:none}}"},s);a.emit("gem:rail:ready",{source:"gem-runtime",count:g})})}function h(e,t){return d(_,v,e,t)}var N=h;export{N as default,_ as key,h as mount,P as readyEvent};
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "recently-viewed";
|
|
6
6
|
export const readyEvent = "gem:recently-viewed:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('recently-viewed', ({ api }) => {
|
|
10
10
|
const storageKey = 'gem:recently-viewed-products';
|
|
11
11
|
const readItems = () => {
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "reveal-on-scroll";
|
|
6
6
|
export const readyEvent = "gem:reveal:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('reveal-on-scroll', ({ api }) => {
|
|
10
10
|
const nodes = document.querySelectorAll('[data-gem-reveal], .gem-reveal');
|
|
11
11
|
let bound = 0;
|
package/dist/plugins/reviews.js
CHANGED
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "reviews";
|
|
6
6
|
export const readyEvent = "gem:reviews:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('reviews', ({ api }) => {
|
|
10
10
|
let boundReviews = 0;
|
|
11
11
|
document.querySelectorAll('[data-rv-root]').forEach((root) => {
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "runtime-slot-loader";
|
|
6
6
|
export const readyEvent = "gem:slot:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('runtime-slot-loader', ({ api }) => {
|
|
10
10
|
const slots = document.querySelectorAll('[data-gem-runtime-slot], [data-gem-footer-block-builder], .gem-navigation-shell__layout');
|
|
11
11
|
let bound = 0;
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "seasonal-effects";
|
|
6
6
|
export const readyEvent = "gem:seasonal:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerNoopPlugin("seasonal-effects", "gem:seasonal:ready");
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// @gemboss/gem-framework/plugins/size-guide: types of the ES module next to this file. Generated; do not edit here.
|
|
2
|
+
import type { MountOptions, Unmount } from "../runtime-core.js";
|
|
3
|
+
|
|
4
|
+
export type { MountOptions, Unmount };
|
|
5
|
+
export declare const key: "size-guide";
|
|
6
|
+
export declare const readyEvent: "gem:size-guide:ready";
|
|
7
|
+
/** Bind every "size-guide" hook inside root (default: the whole document). Returns unmount(). Mounting the same root twice binds once. */
|
|
8
|
+
export declare function mount(root?: ParentNode | null, options?: MountOptions): Unmount;
|
|
9
|
+
export default mount;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @gemboss/gem-framework/plugins/size-guide: the gem-runtime plugin "size-guide" as an ES module. Generated from
|
|
2
|
+
// packages/gem-framework/src/runtime.ts, the same lines gem-runtime.js carries; do not edit here.
|
|
3
|
+
import { mountPlugin } from "../runtime-core.js";
|
|
4
|
+
|
|
5
|
+
export const key = "size-guide";
|
|
6
|
+
export const readyEvent = "gem:size-guide:ready";
|
|
7
|
+
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
|
+
registerPlugin('size-guide', ({ api }) => {
|
|
10
|
+
const page = document.documentElement && typeof document.documentElement.querySelectorAll === 'function' ? document.documentElement : document;
|
|
11
|
+
const guides = Array.prototype.slice.call(page.querySelectorAll('[data-gem-size-guide]'));
|
|
12
|
+
const opens = (target) => guides.some((guide) => !target || guide.getAttribute('data-gem-size-guide') === target || guide.id === target);
|
|
13
|
+
let bound = 0;
|
|
14
|
+
document.querySelectorAll('[data-gem-size-guide-trigger]').forEach((trigger) => {
|
|
15
|
+
const target = trigger.getAttribute('data-gem-size-guide-trigger') || '';
|
|
16
|
+
if (!opens(target)) { trigger.hidden = true; return; }
|
|
17
|
+
if (!mark(trigger, 'size-guide')) return;
|
|
18
|
+
trigger.hidden = false;
|
|
19
|
+
if (!canListen(trigger)) return;
|
|
20
|
+
__gemListen(trigger, 'click', (event) => {
|
|
21
|
+
event.preventDefault && event.preventDefault();
|
|
22
|
+
const holder = trigger.closest ? trigger.closest('[data-product-id]') : null;
|
|
23
|
+
const detail = { source: 'gem-runtime', sectionId: target || null, productId: holder ? holder.getAttribute('data-product-id') : null };
|
|
24
|
+
if (typeof window.CustomEvent === 'function') window.dispatchEvent(new window.CustomEvent('gem:size-guide:open', { detail }));
|
|
25
|
+
api.emit('gem:size-guide:open', detail);
|
|
26
|
+
});
|
|
27
|
+
bound += 1;
|
|
28
|
+
});
|
|
29
|
+
api.emit('gem:size-guide:ready', { source: 'gem-runtime', count: bound, guides: guides.length });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Bind every "size-guide" hook inside root (default: the whole document). Returns unmount(). Mounting the same root twice binds once. */
|
|
34
|
+
export function mount(root, options) {
|
|
35
|
+
return mountPlugin(key, install, root, options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default mount;
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "sticky-atc";
|
|
6
6
|
export const readyEvent = "gem:sticky-atc:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('sticky-atc', ({ api }) => {
|
|
10
10
|
const bars = document.querySelectorAll('[data-gem-sticky-atc], .gem-sticky-atc');
|
|
11
11
|
let bound = 0;
|
package/dist/plugins/tabs.js
CHANGED
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "tabs";
|
|
6
6
|
export const readyEvent = "gem:tabs:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('tabs', ({ api }) => {
|
|
10
10
|
const roots = document.querySelectorAll('[data-gem-tabs]');
|
|
11
11
|
let bound = 0;
|
|
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
|
|
|
5
5
|
export const key = "variant-selects";
|
|
6
6
|
export const readyEvent = "gem:variant:ready";
|
|
7
7
|
|
|
8
|
-
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin }) {
|
|
8
|
+
function install({ document, window, target, mark, canListen, listen: __gemListen, registerPlugin, registerNoopPlugin, onUnmount }) {
|
|
9
9
|
registerPlugin('variant-selects', ({ api }) => {
|
|
10
10
|
const selects = document.querySelectorAll('[data-gem-variant-select], select[name="id"]');
|
|
11
11
|
let bound = 0;
|
package/dist/popup-shell.css
CHANGED
|
@@ -58,6 +58,19 @@
|
|
|
58
58
|
.gem-popup-shell[data-gem-popup-media='background']:is([data-gem-popup-desktop='split-left'], [data-gem-popup-desktop='split-right'], [data-gem-popup-desktop='card']) .gem-popup-shell__close { color: var(--gem-color-text); }
|
|
59
59
|
.gem-popup-shell[data-gem-popup-media='background']:is([data-gem-popup-desktop='split-left'], [data-gem-popup-desktop='split-right'], [data-gem-popup-desktop='card']) .gem-popup-shell__media::after { content: none; }
|
|
60
60
|
}
|
|
61
|
+
@keyframes gem-popup-drawer-in { from { translate: calc(100% * var(--gem-popup-drawer-dir, 1)) 0; } }
|
|
62
|
+
.gem-popup-shell__dialog:dir(rtl) { --gem-popup-drawer-dir: -1; }
|
|
63
|
+
@media (min-width: 750px) {
|
|
64
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog {
|
|
65
|
+
width: min(var(--gem-popup-drawer-width, 640px), 100vw); max-width: 100vw; height: 100dvh; max-height: 100dvh;
|
|
66
|
+
margin-block: 0; margin-inline: auto 0; border-radius: 0;
|
|
67
|
+
animation: gem-popup-drawer-in var(--gem-motion-base, 180ms) var(--gem-ease-enter, ease-out);
|
|
68
|
+
}
|
|
69
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__frame { grid-template-columns: 1fr; align-content: start; min-height: 100%; }
|
|
70
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__media { max-height: 240px; }
|
|
71
|
+
.gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__panel { justify-content: flex-start; align-items: stretch; text-align: start; }
|
|
72
|
+
}
|
|
73
|
+
@media (prefers-reduced-motion: reduce) { .gem-popup-shell[data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog { animation: none; } }
|
|
61
74
|
@media (max-width: 749px) {
|
|
62
75
|
.gem-popup-shell__dialog { width: 100vw; max-width: 100vw; height: 100dvh; max-height: 100dvh; margin: 0; border-radius: 0; }
|
|
63
76
|
.gem-popup-shell[data-gem-popup-mobile='sheet'] .gem-popup-shell__dialog { height: auto; max-height: 88dvh; margin: auto 0 0; border-radius: var(--gem-radius-lg) var(--gem-radius-lg) 0 0; }
|
|
@@ -81,6 +94,8 @@
|
|
|
81
94
|
@media (min-width: 750px) {
|
|
82
95
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='fullscreen'] { padding: 0; }
|
|
83
96
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='fullscreen'] .gem-popup-shell__dialog { height: 720px; max-height: none; }
|
|
97
|
+
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='drawer-end'] { padding-inline: 0; }
|
|
98
|
+
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'][data-gem-popup-desktop='drawer-end'] .gem-popup-shell__dialog { height: 720px; max-height: none; animation: none; }
|
|
84
99
|
}
|
|
85
100
|
@media (max-width: 749px) {
|
|
86
101
|
:root:has([data-gem-canvas]) .gem-popup-shell[data-gem-popup-preview='true'] { padding: 0; }
|
package/dist/primitives.css
CHANGED
|
@@ -103,6 +103,9 @@ a {
|
|
|
103
103
|
cursor: not-allowed;
|
|
104
104
|
opacity: 0.55;
|
|
105
105
|
}
|
|
106
|
+
[data-gem-button-shape='square'] { --gem-radius-button: 0px; }
|
|
107
|
+
[data-gem-button-shape='soft'] { --gem-radius-button: var(--gem-radius-sm); }
|
|
108
|
+
[data-gem-button-shape='pill'] { --gem-radius-button: 999px; }
|
|
106
109
|
.gem-input:disabled, .gem-select:disabled, .gem-textarea:disabled, .gem-quantity-input__button:disabled {
|
|
107
110
|
cursor: not-allowed;
|
|
108
111
|
opacity: 0.6;
|
|
@@ -1068,7 +1071,7 @@ a {
|
|
|
1068
1071
|
:where(.gem-mark--alert) { color: var(--gem-color-danger, #b42318); }
|
|
1069
1072
|
:where(.gem-mark--highlight.gem-mark--accent), :where(.gem-mark--highlight.gem-mark--alert) { color: var(--gem-mark-highlight-ink, #1f1d18); }
|
|
1070
1073
|
.gem-mark-link { color: var(--gem-color-accent); text-decoration-line: underline; text-decoration-thickness: from-font; text-underline-offset: 0.18em; }
|
|
1071
|
-
.gem-mark-link:hover { text-decoration-thickness: 2px; }
|
|
1074
|
+
.gem-mark-link:hover { text-decoration-thickness: var(--gem-underline-thickness, 2px); }
|
|
1072
1075
|
.gem-mark-link.gem-mark--highlight, .gem-mark--highlight .gem-mark-link { color: var(--gem-mark-highlight-ink, #1f1d18); }
|
|
1073
1076
|
@keyframes gem-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(calc(-1 * var(--gem-ambient-amp, 7px))); } }
|
|
1074
1077
|
@keyframes gem-sheen { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.gem-rail { display: block; position: relative; container-type: inline-size; background-color: var(--gem-motion-tint, transparent); }
|
|
2
|
+
.gem-rail__stage { position: relative; height: var(--gem-rail-stage-height, clamp(380px, 62vh, 640px)); overflow: hidden; container-type: size; }
|
|
3
|
+
.gem-rail__rod { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
|
|
4
|
+
.gem-rail__rod path { fill: none; stroke: var(--gem-rail-rod, currentColor); stroke-width: 5; stroke-linecap: round; vector-effect: non-scaling-stroke; opacity: 0.85; }
|
|
5
|
+
.gem-rail__track { list-style: none; margin: 0; padding: 0; position: absolute; inset: 0; }
|
|
6
|
+
.gem-rail__item {
|
|
7
|
+
--gem-rail-t: calc(0.04 + 0.92 * (var(--gem-rail-i, 0) + 0.5) / var(--gem-rail-n, 1));
|
|
8
|
+
position: absolute; left: 0; top: 0; width: 0; height: 0; z-index: 10;
|
|
9
|
+
transform: translate3d(calc(var(--gem-rail-t) * 100cqw), calc((0.14 + 4 * var(--gem-rail-sag, 0.07) * var(--gem-rail-t) * (1 - var(--gem-rail-t))) * 100cqh), 0);
|
|
10
|
+
}
|
|
11
|
+
.gem-rail__hit {
|
|
12
|
+
position: absolute; left: 0; top: -6px; width: calc(var(--gem-rail-height, 0.7) * 78cqh); height: calc(var(--gem-rail-height, 0.7) * 100cqh + 34px);
|
|
13
|
+
padding: 0; border: 0; color: inherit; background: transparent; text-decoration: none; cursor: pointer; transform: translateX(-50%);
|
|
14
|
+
}
|
|
15
|
+
.gem-rail__hit:focus-visible { outline: var(--gem-focus-ring); outline-offset: var(--gem-focus-offset); border-radius: var(--gem-radius-md); }
|
|
16
|
+
.gem-rail__body { position: absolute; inset: 0; display: block; transform-origin: 50% 0; }
|
|
17
|
+
.gem-rail__hook {
|
|
18
|
+
position: absolute; left: calc(50% - 8px); top: 0; width: 16px; height: 22px; opacity: 0.8;
|
|
19
|
+
border: var(--gem-stroke-thick, 2px) solid var(--gem-rail-rod, currentColor); border-bottom: 0; border-radius: var(--gem-radius-md) var(--gem-radius-md) 0 0;
|
|
20
|
+
}
|
|
21
|
+
.gem-rail__hook::after { content: ''; position: absolute; left: calc(50% - 23px); top: 18px; width: 46px; height: var(--gem-stroke-thick, 2px); background: var(--gem-rail-rod, currentColor); border-radius: var(--gem-radius-sm); }
|
|
22
|
+
.gem-rail__tag {
|
|
23
|
+
position: absolute; left: calc(50% + 14px); top: 18px; z-index: 2; padding: var(--gem-space-2xs) var(--gem-space-xs); transform: rotate(4deg);
|
|
24
|
+
font-family: var(--gem-font-mono); font-size: var(--gem-text-xs); font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums;
|
|
25
|
+
color: var(--gem-color-text); background: var(--gem-color-background); border-radius: var(--gem-radius-sm); box-shadow: var(--gem-shadow-sm);
|
|
26
|
+
}
|
|
27
|
+
.gem-rail .gem-turn { position: absolute; inset: 26px 0 0; width: auto; height: auto; }
|
|
28
|
+
.gem-rail__caption { margin: var(--gem-space-xs) 0 0; min-height: 1.5em; text-align: center; font-variant-numeric: tabular-nums; }
|
|
29
|
+
@container (max-width: 699px) {
|
|
30
|
+
.gem-rail__stage { height: auto; overflow: visible; container-type: normal; }
|
|
31
|
+
.gem-rail__rod { display: none; }
|
|
32
|
+
.gem-rail__track { position: static; display: flex; gap: var(--gem-space-2xs); overflow-x: auto; scroll-snap-type: x mandatory; padding: 0 20cqw var(--gem-space-xs); scrollbar-width: none; }
|
|
33
|
+
.gem-rail__track::-webkit-scrollbar { display: none; }
|
|
34
|
+
.gem-rail__item { position: relative; flex: 0 0 60cqw; width: auto; height: calc(72cqw + 34px); transform: none; scroll-snap-align: center; border-top: var(--gem-stroke-thick, 4px) solid var(--gem-rail-rod, currentColor); }
|
|
35
|
+
.gem-rail__hit { left: 50%; width: 100%; height: 100%; }
|
|
36
|
+
.gem-rail__item:first-child .gem-turn__side { opacity: 0; transform: perspective(900px) rotateY(-82deg); }
|
|
37
|
+
.gem-rail__item:first-child .gem-turn:has(> .gem-turn__side) > .gem-turn__front { opacity: 1; transform: scale(1); }
|
|
38
|
+
.gem-rail__item:first-child .gem-turn:not(:has(> .gem-turn__side)) > .gem-turn__front { transform: perspective(900px) rotateY(0deg) scale(1); }
|
|
39
|
+
.gem-rail__item:first-child .gem-turn__sheet { transform: translate(calc(-100% / var(--gem-turn-cols, 1) * mod(var(--gem-turn-frames, 1) - 1, var(--gem-turn-cols, 1))), calc(-100% / var(--gem-turn-rows, 1) * round(down, (var(--gem-turn-frames, 1) - 1) / var(--gem-turn-cols, 1)))); }
|
|
40
|
+
}
|
|
41
|
+
.gem-turn { position: relative; display: block; width: 100%; height: 100%; }
|
|
42
|
+
.gem-turn[data-gem-turn-frames] { overflow: hidden; }
|
|
43
|
+
.gem-turn__side, .gem-turn__front { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: contain; object-position: 50% 0; pointer-events: none; }
|
|
44
|
+
.gem-turn__side { transform: perspective(900px) rotateY(0deg); backface-visibility: hidden; }
|
|
45
|
+
.gem-turn:has(> .gem-turn__side) > .gem-turn__front { opacity: 0; transform: scale(0.9); }
|
|
46
|
+
.gem-turn:not(:has(> .gem-turn__side)) > .gem-turn__front { transform: perspective(900px) rotateY(-74deg) scale(0.86); }
|
|
47
|
+
.gem-turn__sheet { position: absolute; left: 0; top: 0; display: block; max-width: none; pointer-events: none; width: calc(var(--gem-turn-cols, 1) * 100%); height: calc(var(--gem-turn-rows, 1) * 100%); }
|
|
48
|
+
.gem-turn__sheet + .gem-turn__sheet { opacity: 0; }
|
package/dist/runtime-core.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ export interface PluginInstallScope {
|
|
|
38
38
|
listen(element: EventTarget, type: string, callback: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
|
39
39
|
registerPlugin(key: string, setup: (context: { api: GemApi; config: unknown; reason: string }) => void): void;
|
|
40
40
|
registerNoopPlugin(key: string, readyEvent?: string): void;
|
|
41
|
+
/** Run `cleanup` at unmount (0.19.0), for what is not a listener, timer or observer. */
|
|
42
|
+
onUnmount(cleanup: () => void): void;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export declare function createGemApi(doc?: Document): GemApi;
|
package/dist/runtime-core.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
// that body the names it expects (`document`, `window`, `target`, `mark`, `canListen`, `registerPlugin`) scoped to
|
|
5
5
|
// one root, and keeps a ledger of what the body did so unmount() can undo it:
|
|
6
6
|
// - listeners (the module wraps every `x.addEventListener(` of the body as `__gemListen(x, `), timers,
|
|
7
|
-
// animation frames and
|
|
7
|
+
// animation frames and Intersection / Resize observers made through `window` / `target`;
|
|
8
|
+
// - what the body hands to onUnmount(fn) (0.19.0);
|
|
8
9
|
// - the `data-gem-bound-<key>` marks it set, so the same root can be mounted again after an unmount.
|
|
9
10
|
// Importing this file does nothing: no global is read or written until mount() is called.
|
|
10
11
|
|
|
@@ -52,6 +53,21 @@ function scopedDocument(root, doc) {
|
|
|
52
53
|
});
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
// An observer class whose instances the ledger disconnects at unmount.
|
|
57
|
+
function trackedObserver(Base, ledger) {
|
|
58
|
+
if (typeof Base !== "function") return undefined;
|
|
59
|
+
return class extends Base {
|
|
60
|
+
constructor(callback, init) {
|
|
61
|
+
super(callback, init);
|
|
62
|
+
ledger.observers.add(this);
|
|
63
|
+
}
|
|
64
|
+
disconnect() {
|
|
65
|
+
ledger.observers.delete(this);
|
|
66
|
+
super.disconnect();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
// `window` / `target` for a plugin: the real window, except that timers, frames and observers made through it are
|
|
56
72
|
// remembered so unmount() can stop them.
|
|
57
73
|
function trackedWindow(win, ledger) {
|
|
@@ -75,18 +91,9 @@ function trackedWindow(win, ledger) {
|
|
|
75
91
|
return id;
|
|
76
92
|
}
|
|
77
93
|
: undefined,
|
|
78
|
-
IntersectionObserver:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
super(callback, init);
|
|
82
|
-
ledger.observers.add(this);
|
|
83
|
-
}
|
|
84
|
-
disconnect() {
|
|
85
|
-
ledger.observers.delete(this);
|
|
86
|
-
super.disconnect();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
: undefined,
|
|
94
|
+
IntersectionObserver: trackedObserver(win.IntersectionObserver, ledger),
|
|
95
|
+
// 0.19.0: the motion effects watch their root's size.
|
|
96
|
+
ResizeObserver: trackedObserver(win.ResizeObserver, ledger),
|
|
90
97
|
};
|
|
91
98
|
return new Proxy(win, {
|
|
92
99
|
get: (real, prop) => (Object.prototype.hasOwnProperty.call(own, prop) ? own[prop] : bindMethod(real, Reflect.get(real, prop), prop)),
|
|
@@ -116,7 +123,7 @@ export function mountPlugin(key, install, root, options = {}, guard) {
|
|
|
116
123
|
const doc = scope.nodeType === 9 ? scope : scope.ownerDocument || globalThis.document;
|
|
117
124
|
const win = options.window || (doc && doc.defaultView) || globalThis;
|
|
118
125
|
const api = options.api || (doc === globalThis.document ? gemApi() : createGemApi(doc));
|
|
119
|
-
const ledger = { listeners: [], marks: [], timeouts: new Set(), intervals: new Set(), frames: new Set(), observers: new Set() };
|
|
126
|
+
const ledger = { listeners: [], marks: [], timeouts: new Set(), intervals: new Set(), frames: new Set(), observers: new Set(), cleanups: [] };
|
|
120
127
|
const window = trackedWindow(win, ledger);
|
|
121
128
|
const document = scope === doc ? doc : scopedDocument(scope, doc);
|
|
122
129
|
// `data-gem-bound-<key>` with ANY value means bound, including one the markup carries to claim an element for its own
|
|
@@ -140,7 +147,11 @@ export function mountPlugin(key, install, root, options = {}, guard) {
|
|
|
140
147
|
};
|
|
141
148
|
const unguarded = guard ? Array.from(document.querySelectorAll(guard.selector)).filter((element) => !element[guard.prop]) : [];
|
|
142
149
|
|
|
150
|
+
// 0.19.0: a plugin hands back what is not a listener, timer or observer (the motion effects' scheduler tasks, host API).
|
|
151
|
+
const onUnmount = (fn) => { if (typeof fn === "function") ledger.cleanups.push(fn); };
|
|
152
|
+
|
|
143
153
|
const teardown = () => {
|
|
154
|
+
ledger.cleanups.splice(0).reverse().forEach((fn) => fn());
|
|
144
155
|
ledger.timeouts.forEach((id) => win.clearTimeout(id));
|
|
145
156
|
ledger.intervals.forEach((id) => win.clearInterval(id));
|
|
146
157
|
ledger.frames.forEach((id) => win.cancelAnimationFrame && win.cancelAnimationFrame(id));
|
|
@@ -150,7 +161,7 @@ export function mountPlugin(key, install, root, options = {}, guard) {
|
|
|
150
161
|
if (guard) unguarded.forEach((element) => { delete element[guard.prop]; });
|
|
151
162
|
};
|
|
152
163
|
try {
|
|
153
|
-
install({ document, window, target: window, mark, canListen, listen, registerPlugin, registerNoopPlugin });
|
|
164
|
+
install({ document, window, target: window, mark, canListen, listen, registerPlugin, registerNoopPlugin, onUnmount });
|
|
154
165
|
if (typeof setup === "function") {
|
|
155
166
|
setup({ api, config: { planId: "gem-plugin-module", plugins: [{ key, loadStrategy: "mount", dependsOn: [] }] }, reason: "mount" });
|
|
156
167
|
}
|