@gemboss/gem-framework 0.18.16 → 0.20.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.
Files changed (68) hide show
  1. package/README.md +109 -1
  2. package/dist/SOURCE.json +3 -3
  3. package/dist/gem-framework.css +23 -1
  4. package/dist/gem-runtime.js +97 -54
  5. package/dist/index.js +2511 -44
  6. package/dist/motion/core.d.ts +208 -0
  7. package/dist/motion/core.js +1 -0
  8. package/dist/motion/orbit.d.ts +4 -0
  9. package/dist/motion/orbit.js +1 -0
  10. package/dist/motion/quick-view.d.ts +4 -0
  11. package/dist/motion/quick-view.js +1 -0
  12. package/dist/motion/rail.d.ts +5 -0
  13. package/dist/motion/rail.js +1 -0
  14. package/dist/orbit-base.css +39 -0
  15. package/dist/plugins/before_after.js +1 -1
  16. package/dist/plugins/bundle.js +1 -1
  17. package/dist/plugins/carousel.js +1 -1
  18. package/dist/plugins/cart-drawer.js +1 -1
  19. package/dist/plugins/context-adapter.js +1 -1
  20. package/dist/plugins/countdown.js +1 -1
  21. package/dist/plugins/delivery-estimate.d.ts +9 -0
  22. package/dist/plugins/delivery-estimate.js +1 -0
  23. package/dist/plugins/disclosure.js +1 -1
  24. package/dist/plugins/hero-slideshow.js +1 -1
  25. package/dist/plugins/img-in.js +1 -1
  26. package/dist/plugins/localization.js +1 -1
  27. package/dist/plugins/media-gallery.js +1 -1
  28. package/dist/plugins/mobile-menu.js +1 -1
  29. package/dist/plugins/offer-engine.js +1 -1
  30. package/dist/plugins/orbit.d.ts +9 -0
  31. package/dist/plugins/orbit.js +1 -0
  32. package/dist/plugins/personalize.d.ts +9 -0
  33. package/dist/plugins/personalize.js +1 -0
  34. package/dist/plugins/predictive-search.js +1 -1
  35. package/dist/plugins/print-preview.d.ts +9 -0
  36. package/dist/plugins/print-preview.js +1 -0
  37. package/dist/plugins/promo-popup.js +1 -1
  38. package/dist/plugins/quantity-input.js +1 -1
  39. package/dist/plugins/quick-view.js +1 -69
  40. package/dist/plugins/rail.d.ts +9 -0
  41. package/dist/plugins/rail.js +1 -0
  42. package/dist/plugins/recently-viewed.js +1 -1
  43. package/dist/plugins/reveal-on-scroll.js +1 -1
  44. package/dist/plugins/reviews.js +1 -1
  45. package/dist/plugins/runtime-slot-loader.js +1 -1
  46. package/dist/plugins/seasonal-effects.js +1 -1
  47. package/dist/plugins/size-guide.d.ts +9 -0
  48. package/dist/plugins/size-guide.js +38 -0
  49. package/dist/plugins/sticky-atc.js +1 -1
  50. package/dist/plugins/tabs.js +1 -1
  51. package/dist/plugins/variant-selects.js +1 -1
  52. package/dist/pod/core.d.ts +26 -0
  53. package/dist/pod/core.js +1 -0
  54. package/dist/pod/delivery-estimate.d.ts +28 -0
  55. package/dist/pod/delivery-estimate.js +1 -0
  56. package/dist/pod/personalize.d.ts +25 -0
  57. package/dist/pod/personalize.js +1 -0
  58. package/dist/pod/print-preview.d.ts +8 -0
  59. package/dist/pod/print-preview.js +1 -0
  60. package/dist/pod-buybox.css +55 -0
  61. package/dist/popup-shell.css +15 -0
  62. package/dist/primitives.css +4 -1
  63. package/dist/rail-base.css +48 -0
  64. package/dist/runtime-core.d.ts +2 -0
  65. package/dist/runtime-core.js +26 -15
  66. package/dist/section-gate.js +2522 -36
  67. package/dist/tokens.css +4 -0
  68. package/package.json +20 -1
@@ -0,0 +1,208 @@
1
+ // Types for @gemboss/gem-framework/motion/core (core.js, copied as is into dist/motion/). Hand-written next to the file it
2
+ // describes, like runtime-core.d.ts.
3
+
4
+ export type GemMotionTier = "static" | "light" | "full";
5
+
6
+ export interface RingOptions {
7
+ /** horizontal radius, px */
8
+ rx: number;
9
+ /** vertical rise of the back of the ring, px */
10
+ ry: number;
11
+ /** scale of the piece at the very back (front = 1) */
12
+ back: number;
13
+ /** opacity at the very back */
14
+ fade: number;
15
+ /** blur at the very back, px */
16
+ blur: number;
17
+ /** degrees a piece leans as it swings to the side */
18
+ lean: number;
19
+ /** angle between neighbours, radians; 2π/n spreads the pieces round the full ring */
20
+ spread: number;
21
+ }
22
+
23
+ export interface RingPose {
24
+ x: number;
25
+ y: number;
26
+ /** 0 at the front, 1 at the very back */
27
+ depth: number;
28
+ scale: number;
29
+ opacity: number;
30
+ blur: number;
31
+ z: number;
32
+ lean: number;
33
+ hidden: boolean;
34
+ }
35
+
36
+ export interface GemMotionImage {
37
+ src: string;
38
+ srcset?: string;
39
+ width?: number;
40
+ height?: number;
41
+ alt?: string;
42
+ }
43
+
44
+ /** What `data-gem-product` holds (JSON). */
45
+ export interface GemMotionProduct {
46
+ id: string | number;
47
+ title: string;
48
+ url?: string;
49
+ /** the price as the card shows it (cardPrice); shown verbatim */
50
+ price_text?: string;
51
+ compare_at_text?: string;
52
+ /** fallback for a host without cardPrice: minor units, formatted with the nearest data-gem-money-format */
53
+ price?: number;
54
+ compare_at?: number;
55
+ sold_out?: boolean;
56
+ eyebrow?: string;
57
+ description?: string;
58
+ /** a CSS colour the band (and the quick view's surface) takes while this piece is in front */
59
+ tint?: string;
60
+ image: GemMotionImage;
61
+ /** the variant added to the cart when the product has no colour, size or variant list */
62
+ variant_id?: string | number;
63
+ colors?: Array<{ name: string; hex?: string; tint?: string; image?: GemMotionImage; side?: GemMotionImage; variant_ids?: Array<string | number> }>;
64
+ sizes?: Array<{ name: string; available: boolean; variant_id?: string | number }>;
65
+ /** Shopify variants: id sent to the cart as given (a gid stays a gid); options are selectedOptions values */
66
+ variants?: Array<{ id: string | number; available: boolean; options: string[]; price_text?: string; compare_at_text?: string; price?: number; compare_at?: number }>;
67
+ }
68
+
69
+ /** One setting of an effect, read from `data-gem-<effect>-<name>`. An editor generates its control from this. */
70
+ export type GemMotionSetting =
71
+ | { type: "number" | "integer"; default: number; min?: number; max?: number; step?: number; label: string; css?: boolean; inherit?: boolean }
72
+ | { type: "boolean"; default: boolean; label: string; css?: boolean; inherit?: boolean }
73
+ | { type: "enum"; default: string; options: string[]; label: string; css?: boolean; inherit?: boolean }
74
+ | { type: "text"; default: string; label: string; css?: boolean; inherit?: boolean }
75
+ | { type: "json"; default: unknown; label: string; css?: boolean; inherit?: boolean };
76
+
77
+ /**
78
+ * A renderer draws an effect. `dom` (built in) moves the server-rendered elements with transform and opacity.
79
+ *
80
+ * Planned, each its own lazily loaded chunk that a page without such an effect never downloads (the effect module calls
81
+ * `defineRenderer(key, () => import("./renderers/<key>.js"))`, and nothing loads until that effect mounts):
82
+ * - `canvas2d`: image sequences and turntables (AI-generated frames, loaded through loadImage), particles;
83
+ * - `video`: scrubs a muted, inline <video> by currentTime against scroll or drag progress;
84
+ * - `webgl`: real 3D, e.g. a glTF product viewer.
85
+ * A non-dom renderer draws into a surface it creates inside the root; the effect's `fallback` (the server render, or the
86
+ * section's poster) stays on screen until it is ready and whenever it cannot load.
87
+ */
88
+ export interface GemMotionRenderer {
89
+ key: "dom" | "canvas2d" | "video" | "webgl" | (string & {});
90
+ /** dom: write one style property, skipped when it did not change. */
91
+ style?(el: HTMLElement | SVGElement, prop: string, value: string): void;
92
+ /** canvas2d / video / webgl: make a drawing surface inside `root`. */
93
+ createSurface?(root: Element, ctx: GemMotionContext): GemMotionSurface;
94
+ }
95
+
96
+ export interface GemMotionSurface {
97
+ /** css px and device pixel ratio, on mount and on every size change */
98
+ resize(width: number, height: number, dpr: number): void;
99
+ /** draw one frame of the effect's state (called from the effect's frame task) */
100
+ draw(state: unknown): void;
101
+ destroy(): void;
102
+ }
103
+
104
+ export interface GemMotionTask {
105
+ kick(): void;
106
+ visible(value: boolean): void;
107
+ remove(): void;
108
+ }
109
+
110
+ export interface GemMotionScheduler {
111
+ /** true once frames ran long (> 20 ms, 30 in a row): effects drop to `light` */
112
+ readonly degraded: boolean;
113
+ readonly size: number;
114
+ add(step: (dt: number, now: number) => boolean, options?: { onDegrade?: () => void }): GemMotionTask;
115
+ }
116
+
117
+ export interface GemMotionEnv {
118
+ window: Window;
119
+ document: Document;
120
+ on(target: EventTarget | null | undefined, type: string, listener: (event: any) => void, options?: boolean | AddEventListenerOptions): void;
121
+ mark(element: Element, key: string): boolean;
122
+ api: { emit(event: string, detail?: Record<string, unknown>): void };
123
+ /** the effect's runtime stylesheet (adopted once per document unless the page carries it) */
124
+ css?: string;
125
+ }
126
+
127
+ export interface GemMotionContext extends GemMotionEnv {
128
+ root: Element;
129
+ /** what the roots of one plugin mount share; its cleanups run at unmount */
130
+ session: { cleanups: Array<() => void>; [key: string]: unknown };
131
+ scheduler: GemMotionScheduler;
132
+ /** the tier now: the device's, lowered to light when the frame guard trips */
133
+ tier(): GemMotionTier;
134
+ settings: Record<string, any>;
135
+ reread(): Record<string, any>;
136
+ renderer: GemMotionRenderer;
137
+ cleanup(fn: () => void): void;
138
+ emit(type: string, detail?: unknown): void;
139
+ }
140
+
141
+ export interface GemMotionEffectSpec {
142
+ /** kebab-case; the plugin key, the mark (data-gem-bound-<key>) and the settings prefix (data-gem-<key>-<name>) */
143
+ key: string;
144
+ /** the selector of a root; default [data-gem-<key>] */
145
+ hook?: string;
146
+ /** when gem-runtime.js imports the chunk: when a hook nears the viewport, or on the first interaction (or idle) */
147
+ load?: "viewport" | "interaction";
148
+ /** default dom */
149
+ renderer?: GemMotionRenderer["key"];
150
+ /** the lowest tier it runs in; below it the fallback stays. Default static (it adapts down to instant moves) */
151
+ tier?: GemMotionTier;
152
+ /** required for a heavy effect (tier above static, or a renderer other than dom) */
153
+ fallback?: "markup" | "poster";
154
+ /** gzip bytes of its chunk (plugin + behaviour + core), checked by the package build test */
155
+ budget?: { gzip: number };
156
+ settings?: Record<string, GemMotionSetting>;
157
+ /** bind one root; return a cleanup, or false to stay on the fallback */
158
+ mount(root: Element, ctx: GemMotionContext): (() => void) | false | void;
159
+ }
160
+
161
+ export type GemMotionEffect = Required<Omit<GemMotionEffectSpec, "budget">> & Pick<GemMotionEffectSpec, "budget"> & { declared: GemMotionEffectSpec };
162
+
163
+ export declare const TIERS: GemMotionTier[];
164
+ export declare function clamp(v: number, lo: number, hi: number): number;
165
+ export declare function lerp(a: number, b: number, t: number): number;
166
+ export declare function mod(v: number, n: number): number;
167
+ export declare function smoothstep(edge0: number, edge1: number, v: number): number;
168
+ export declare function follow(dt: number, tau: number): number;
169
+ export declare function spring(state: { x: number; v: number }, target: number, dt: number, stiffness: number, damping: number): { x: number; v: number };
170
+ export declare function ringDelta(a: number, b: number, n: number): number;
171
+ export declare function ringPose(i: number, n: number, pos: number, o: RingOptions): RingPose;
172
+ export declare function throwTarget(pos: number, velocity: number, carry: number, maxSlots: number): number;
173
+ export declare function scrollProgress(rect: { top: number; height: number }, viewportHeight: number): number;
174
+ export declare function reducedMotion(win: Window | null | undefined): boolean;
175
+ export declare function capabilityTier(win: Window | null | undefined): GemMotionTier;
176
+ export declare function schedulerFor(doc: Document): GemMotionScheduler;
177
+ export declare function setStyle(el: HTMLElement | SVGElement, prop: string, value: string): void;
178
+ export declare const domRenderer: GemMotionRenderer;
179
+ export declare function defineRenderer(key: string, loader: () => GemMotionRenderer | Promise<GemMotionRenderer>): void;
180
+ export declare function loadRenderer(key: string): Promise<GemMotionRenderer>;
181
+ export declare function defineEffect(spec: GemMotionEffectSpec): GemMotionEffect;
182
+ export declare function checkEffect(effect: GemMotionEffect | GemMotionEffectSpec): string[];
183
+ export declare function gemMotionEffects(): GemMotionEffect[];
184
+ export declare function readSettings(root: Element, effect: Pick<GemMotionEffect, "key" | "settings">): Record<string, any>;
185
+ export declare function mountEffect(effect: GemMotionEffect, root: Element, env: GemMotionEnv, session: { cleanups: Array<() => void> }): boolean;
186
+ export declare function mountEffects(effect: GemMotionEffect, env: GemMotionEnv, onUnmount: (fn: () => void) => void): number;
187
+ export declare function frameTask(ctx: GemMotionContext, el: Element, step: (dt: number, now: number) => boolean, options?: { onDegrade?: () => void }): GemMotionTask;
188
+ export declare function observeVisibility(ctx: Pick<GemMotionContext, "window">, el: Element, cb: (shown: boolean) => void, margin?: string): void;
189
+ export declare function observeSize(ctx: Pick<GemMotionContext, "window" | "on">, el: Element, cb: () => void): void;
190
+ export declare function horizontalDrag(
191
+ ctx: Pick<GemMotionContext, "on">,
192
+ el: HTMLElement,
193
+ handlers: { start(e: PointerEvent): void; move(dx: number, e: PointerEvent): void; end(vx: number, moved: number, e: PointerEvent): void },
194
+ ): void;
195
+ export declare function wheelSteps(ctx: Pick<GemMotionContext, "on">, el: Element, onStep: (direction: number) => void): void;
196
+ export declare function arrowKeys(
197
+ ctx: Pick<GemMotionContext, "on">,
198
+ el: Element,
199
+ handlers: { step(direction: number): void; activate?(e: KeyboardEvent): unknown; accept?(e: KeyboardEvent): boolean },
200
+ ): void;
201
+ export declare function flip(el: Element, from: DOMRect | null | undefined, options: { back?: boolean; duration: number; easing: string }): Animation | null;
202
+ export declare function loadImage(doc: Document, image: GemMotionImage): Promise<HTMLImageElement>;
203
+ export declare function putImage(img: HTMLImageElement | null | undefined, image: GemMotionImage | null | undefined): void;
204
+ export declare function swapText(el: Element | null | undefined, text: string, still: boolean): void;
205
+ export declare function readProduct(el: Element | null | undefined): GemMotionProduct | null;
206
+ export declare function formatMoney(cents: number, format?: string): string;
207
+ export declare function priceText(product: GemMotionProduct, el?: Element | null, which?: "price" | "compare"): string;
208
+ export declare function adoptCss(el: Element, key: string, sentinel: string, css: string): boolean;
@@ -0,0 +1 @@
1
+ const y=(t,e,n)=>t<e?e:t>n?n:t,w=(t,e,n)=>t+(e-t)*n,I=(t,e)=>(t%e+e)%e;function B(t,e,n){const r=y((n-t)/(e-t),0,1);return r*r*(3-2*r)}const J=(t,e)=>e<=0?1:1-Math.exp(-t/e);function G(t,e,n,r,s){const o=n/16.67;return t.v+=(-(t.x-e)*r-t.v*s)*o,t.x+=t.v*o,t}function z(t,e,n){let r=I(e-t,n);return r>n/2&&(r-=n),r}function K(t,e,n,r){const s=z(n,t,e)*r.spread,o=Math.abs(s)>Math.PI,a=(1-Math.cos(s))/2;return{x:r.rx*Math.sin(s),y:-r.ry*a,depth:a,scale:w(1,r.back,a),opacity:o?0:w(1,r.fade,a),blur:r.blur*a,z:Math.round((1-a)*100),lean:Math.sin(s)*r.lean,hidden:o}}const H=(t,e,n,r)=>Math.round(t+y(e*n,-r,r)),Q=(t,e)=>y((e-t.top)/(e+t.height),0,1),b=["static","light","full"],g=t=>Math.max(0,b.indexOf(t)),k=(t,e)=>!!(t&&typeof t.matchMedia=="function"&&t.matchMedia(e).matches),S=t=>k(t,"(prefers-reduced-motion: reduce)");function D(t){if(S(t))return"static";const e=t&&t.navigator||{};return e.connection&&e.connection.saveData||e.deviceMemory&&e.deviceMemory<=2||k(t,"(pointer: coarse)")?"light":"full"}const M=new WeakMap,C=20,O=30;function R(t){let e=M.get(t);return e||M.set(t,e=T(t)),e}function T(t){const e=t.defaultView,n=new Set,r=()=>e.performance?e.performance.now():Date.now();let s=0,o=0,a=0,c=!1;const d=()=>{s&&e.cancelAnimationFrame(s),s=0},l=()=>{if(!(s||t.hidden||typeof e.requestAnimationFrame!="function")){for(const u of n)if(u.awake&&u.shown){o=r(),s=e.requestAnimationFrame(p);return}}},p=u=>{s=0;const m=Math.min(64,Math.max(0,u-o));if(o=u,a=m>C?a+1:0,a>=O&&!c){c=!0;for(const f of n)f.onDegrade&&f.onDegrade()}for(const f of n)f.awake&&f.shown&&(f.awake=f.step(m,u)===!0);l()},i=()=>t.hidden?d():l();return{get degraded(){return c},get size(){return n.size},add(u,m){const f={step:u,awake:!1,shown:!0,onDegrade:m&&m.onDegrade};return n.size||t.addEventListener("visibilitychange",i),n.add(f),{kick(){if(n.has(f)){if(S(e)){f.awake=!1,u(0,r());return}f.awake=!0,l()}},visible(h){f.shown=h,h&&l()},remove(){!n.delete(f)||n.size||(d(),t.removeEventListener("visibilitychange",i))}}}}}const v=new WeakMap;function F(t,e,n){let r=v.get(t);r||v.set(t,r={}),r[e]!==n&&(r[e]=n,n===""?t.style.removeProperty(e):t.style.setProperty(e,n))}const x={key:"dom",style:F},$=new Map([["dom",()=>Promise.resolve(x)]]);function U(t,e){$.set(t,e)}function V(t){const e=$.get(t||"dom");return e?Promise.resolve().then(e):Promise.reject(new Error(`gem-motion: no renderer "${t}"`))}const E=new Map,Y=["number","integer","boolean","enum","text","json"];function Z(t){const e={hook:`[data-gem-${t.key}]`,load:"viewport",renderer:"dom",tier:"static",fallback:"markup",settings:{},...t,declared:t};return E.set(e.key,e),e}function tt(t){const e=t.declared||t,n=String(e.key),r=[];/^[a-z][a-z0-9-]*$/.test(n)||r.push(`key "${n}" is not kebab-case`),typeof e.mount!="function"&&r.push(`${n} has no mount()`),e.tier&&!b.includes(e.tier)&&r.push(`${n}: tier must be one of ${b.join(", ")}`),(e.tier&&e.tier!=="static"||e.renderer&&e.renderer!=="dom")&&!["markup","poster"].includes(e.fallback)&&r.push(`${n} is heavy and must declare a static fallback (markup or poster)`);for(const[s,o]of Object.entries(e.settings||{}))!Y.includes(o.type)||!("default"in o)||!o.label?r.push(`${n}.${s} needs a type, a default and a label`):(o.type==="number"||o.type==="integer")&&(o.min!=null&&o.default<o.min||o.max!=null&&o.default>o.max)?r.push(`${n}.${s}: default out of range`):o.type==="enum"&&!(o.options||[]).includes(o.default)&&r.push(`${n}.${s}: default is not an option`);return r}const et=()=>[...E.values()];function j(t,e){if(e==null||e==="")return t.default;if(t.type==="number"||t.type==="integer"){const n=Number(e);if(!Number.isFinite(n))return t.default;const r=y(n,t.min==null?-1/0:t.min,t.max==null?1/0:t.max);return t.type==="integer"?Math.round(r):r}if(t.type==="boolean")return e!=="false"&&e!=="0"&&e!=="off";if(t.type==="enum")return(t.options||[]).includes(e)?e:t.default;if(t.type==="json")try{return JSON.parse(e)}catch{return t.default}return e}function _(t,e){const n={};let r=null;for(const[s,o]of Object.entries(e.settings)){const a=`data-gem-${e.key}-${s}`,c=`--gem-${e.key}-${s}`,d=o.inherit?t.closest(`[${a}]`):t,l=d?d.getAttribute(a):null;let p="";if(o.css)try{r=r||t.ownerDocument.defaultView.getComputedStyle(t),p=r.getPropertyValue(c).trim()}catch{p=""}n[s]=j(o,p||l),o.css&&!p&&n[s]!==o.default&&t.style.setProperty(c,String(n[s]))}return n}const N=(t,e)=>g(t)<g(e)?t:e;function X(t,e,n,r){const s=e.ownerDocument,o=s.defaultView,a=R(s),c=()=>N(D(o),a.degraded?"light":"full"),d=i=>(e.dispatchEvent(new o.CustomEvent("gem:motion:fallback",{bubbles:!0,detail:{effect:t.key,reason:i,fallback:t.fallback}})),!1);if(g(c())<g(t.tier))return d("tier");n.css&&q(e,t.key,`--gem-${t.key}-css`,n.css);const l={root:e,window:n.window,document:n.document,on:n.on,mark:n.mark,api:n.api,session:r,scheduler:a,tier:c,settings:_(e,t),reread:()=>l.settings=_(e,t),renderer:x,cleanup:i=>r.cleanups.push(i),emit:(i,u)=>e.dispatchEvent(new o.CustomEvent(i,{bubbles:!0,detail:u}))},p=i=>{l.renderer=i;const u=t.mount(e,l);return typeof u=="function"&&r.cleanups.push(u),u!==!1};return t.renderer==="dom"?p(x):(V(t.renderer).then(p,()=>d("renderer")),!0)}function nt(t,e,n){const r={cleanups:[]};let s=0;return e.document.querySelectorAll(t.hook).forEach(o=>{e.mark(o,t.key)&&X(t,o,e,r)&&(s+=1)}),n(()=>r.cleanups.splice(0).reverse().forEach(o=>o())),s}function rt(t,e,n,r){const s=t.scheduler.add(n,r);return t.cleanup(()=>s.remove()),L(t,e,o=>s.visible(o)),s}function L(t,e,n,r){const s=t.window.IntersectionObserver;typeof s=="function"&&new s(o=>n(o.some(a=>a.isIntersecting)),{rootMargin:r||"120px"}).observe(e)}function ot(t,e,n){const r=t.window.ResizeObserver;typeof r=="function"?new r(()=>n()).observe(e):t.on(t.window,"resize",()=>n())}function st(t,e,n){let r=null,s=0,o=0,a=!1,c=!1,d=0,l=[];const p=(i,u)=>{if(i.pointerId!==r)return;r=null;const m=l[0],f=l[l.length-1],h=f.t-m.t,P=h>0&&i.timeStamp-f.t<80?(f.x-m.x)/h:0;u&&c?n.end(P,d,i):n.end(0,0,i)};t.on(e,"pointerdown",i=>{i.button!==0||r!==null||(r=i.pointerId,s=i.clientX,o=i.clientY,a=c=!1,d=0,l=[{x:i.clientX,t:i.timeStamp}],n.start(i))}),t.on(e,"pointermove",i=>{if(i.pointerId!==r)return;const u=i.clientX-s;if(!a&&Math.hypot(u,i.clientY-o)>6){if(a=!0,c=Math.abs(u)>Math.abs(i.clientY-o),!c)return p(i,!1);try{e.setPointerCapture(i.pointerId)}catch{}}if(c){for(d=Math.max(d,Math.abs(u)),l.push({x:i.clientX,t:i.timeStamp});l.length>2&&i.timeStamp-l[0].t>90;)l.shift();n.move(u,i)}}),t.on(e,"pointerup",i=>p(i,!0)),t.on(e,"pointercancel",i=>p(i,!0))}function it(t,e,n){let r=0,s=0;t.on(e,"wheel",o=>{Math.abs(o.deltaX)<=Math.abs(o.deltaY)||o.target.closest&&o.target.closest("dialog")||(o.preventDefault(),r+=o.deltaX,!(o.timeStamp<s||Math.abs(r)<24)&&(n(Math.sign(r)),r=0,s=o.timeStamp+280))},{passive:!1})}function at(t,e,{step:n,activate:r,accept:s}){t.on(e,"keydown",o=>{if(!(s&&!s(o))){if(o.key==="ArrowRight"||o.key==="ArrowLeft")n(o.key==="ArrowRight"?1:-1);else if(!r||o.key!=="Enter"&&o.key!==" "||r(o)===!1)return;o.preventDefault()}})}function ct(t,e,n){if(!e||!e.width||typeof t.animate!="function")return null;const r=t.getBoundingClientRect();if(!r.height)return null;const s={transform:`translate(${e.left+e.width/2-r.left-r.width/2}px, ${e.top+e.height/2-r.top-r.height/2}px) scale(${e.height/r.height})`},o={transform:"none"};return t.animate(n.back?[o,s]:[s,o],{duration:n.duration,easing:n.easing,fill:"both"})}function ut(t,e){const n=t.createElement("img");return e.srcset&&(n.srcset=e.srcset),n.src=e.src,typeof n.decode=="function"?n.decode().then(()=>n,()=>n):Promise.resolve(n)}function lt(t,e){!t||!e||!e.src||t.getAttribute("src")===e.src||(e.srcset?t.setAttribute("srcset",e.srcset):t.removeAttribute("srcset"),t.setAttribute("src",e.src))}function ft(t,e,n){if(!t||t.textContent===e)return;if(n||typeof t.animate!="function"){t.textContent=e;return}const r=t.animate([{opacity:1},{opacity:0,transform:"translateY(-6px)"}],{duration:140,fill:"forwards"});r.onfinish=()=>{t.textContent=e,r.cancel(),t.animate([{opacity:0,transform:"translateY(8px)"},{opacity:1,transform:"none"}],{duration:220,easing:"cubic-bezier(.2,.7,.2,1)"})}}function dt(t){try{const e=JSON.parse(t.getAttribute("data-gem-product")||"null");return e&&e.image&&e.image.src?e:null}catch{return null}}function W(t,e){const n=(Number(t)||0)/100,r=(a,c)=>a.replace(/\B(?=(\d{3})+(?!\d))/g,c),[s,o]=n.toFixed(2).split(".");return(e||"${{amount}}").replace(/\{\{\s*(\w+)\s*\}\}/,(a,c)=>c==="amount_no_decimals"?r(String(Math.round(n)),","):c==="amount_with_comma_separator"?`${r(s,".")},${o}`:c==="amount_no_decimals_with_comma_separator"?r(String(Math.round(n)),"."):`${r(s,",")}.${o}`)}function pt(t,e,n){const r=n==="compare",s=r?t.compare_at_text:t.price_text;if(s!=null)return String(s);const o=r?t.compare_at:t.price;if(o==null||o==="")return"";if(typeof o!="number")return String(o);const a=e&&e.closest?e.closest("[data-gem-money-format]"):null;return W(o,a?a.getAttribute("data-gem-money-format"):void 0)}const A=new WeakMap;function q(t,e,n,r){const s=t.ownerDocument,o=s.defaultView;let a=A.get(s);if(a||A.set(s,a=new Set),a.has(e))return!1;a.add(e);try{if(o.getComputedStyle(t).getPropertyValue(n).trim())return!1;const c=new o.CSSStyleSheet;return c.replaceSync(r),s.adoptedStyleSheets=[...s.adoptedStyleSheets,c],!0}catch{return!1}}export{b as TIERS,q as adoptCss,at as arrowKeys,D as capabilityTier,tt as checkEffect,y as clamp,Z as defineEffect,U as defineRenderer,x as domRenderer,ct as flip,J as follow,W as formatMoney,rt as frameTask,et as gemMotionEffects,st as horizontalDrag,w as lerp,ut as loadImage,V as loadRenderer,I as mod,X as mountEffect,nt as mountEffects,ot as observeSize,L as observeVisibility,pt as priceText,lt as putImage,dt as readProduct,_ as readSettings,S as reducedMotion,z as ringDelta,K as ringPose,R as schedulerFor,Q as scrollProgress,F as setStyle,B as smoothstep,G as spring,ft as swapText,H as throwTarget,it as wheelSteps};
@@ -0,0 +1,4 @@
1
+ // Types for @gemboss/gem-framework/motion/orbit (orbit.js, copied as is into dist/motion/).
2
+ import type { GemMotionEffect } from "./core.js";
3
+
4
+ export declare const orbitEffect: GemMotionEffect;
@@ -0,0 +1 @@
1
+ import{arrowKeys as j,defineEffect as K,follow as L,frameTask as N,horizontalDrag as G,mod as Q,observeSize as J,priceText as U,putImage as Y,readProduct as Z,ringDelta as ee,ringPose as te,swapText as R,throwTarget as ae,wheelSteps as re}from"./core.js";const V=a=>String(a).padStart(2,"0");function ie(a,r){const g=[],A=[],p=[],M=[];a.querySelectorAll("[data-gem-orbit-item]").forEach(e=>{const t=Z(e);t&&(g.push(e),A.push(t),p.push(e.querySelector(".gem-orbit__hit")||e),M.push(e.querySelector("img")))});const s=g.length;if(!s)return!1;const m=e=>a.querySelector(".gem-orbit__"+e),F=m("stage")||a,P=m("track"),h=r.renderer.style;let i=r.settings;const z=Math.min(s-1,i.start);let c=z,u=z,I=0,y=!1,E=!1,O=-1,w=null,T=60;const q=()=>r.tier()==="full",D=()=>{i=r.reread(),w={rx:F.clientWidth*i.rx,ry:F.clientHeight*i.tilt,back:i.back,fade:i.fade,blur:q()?i.blur:0,lean:i.lean,spread:(i.spread||360/s)*Math.PI/180},T=Math.max(60,w.rx*w.spread/1.2)},$=e=>{i.tint&&(e?a.style.setProperty("--gem-motion-tint",e):a.style.removeProperty("--gem-motion-tint"))},v=()=>Q(Math.round(c),s),H=e=>g.findIndex(t=>t===e||t.contains(e)),_=e=>{const t=v();if(t===O)return;O=t;const o=A[t];if(g.forEach((d,n)=>{n===t?d.setAttribute("aria-current","true"):d.removeAttribute("aria-current"),p[n].tabIndex=n===t?0:-1}),!e){const d=r.tier()==="static",n=U(o,a);R(m("name"),o.title||"",d),R(m("price"),n,d);const l=m("count");l&&(l.textContent=V(t+1)+" / "+V(s));const S=m("bar");S&&(S.style.transform="scaleX("+(t+1)/s+")");const f=m("live"),x={index:t+1,count:s,title:o.title||"",price:n};f&&(f.textContent=i.live.replace(/\{(\w+)\}/g,(X,B)=>Object.hasOwn(x,B)?String(x[B]):X)),$(o.tint)}r.emit("gem:motion:front",{index:t,product:o})},W=(e,t)=>{let o=y;if(!y){const n=u-c;r.tier()==="static"||Math.abs(n)<8e-4?c=u:(c+=n*L(e,i.ease),o=!0)}const d=i.drift&&q();for(let n=0;n<s;n++){const l=te(n,s,c,w),S=d?Math.sin(t/1100+n*1.9)*5*(1-l.depth):0,f=g[n];h(f,"transform",`translate3d(${l.x.toFixed(1)}px, ${(l.y+S).toFixed(1)}px, 0) scale(${l.scale.toFixed(3)}) rotate(${l.lean.toFixed(2)}deg)`),h(f,"opacity",l.opacity.toFixed(3)),h(f,"z-index",String(l.z));const x=Math.round(l.blur*2)/2;h(f,"filter",x>0?`blur(${x}px)`:"none"),h(f,"visibility",l.hidden?"hidden":"visible")}return E=!0,_(!1),o||d},C=()=>{D(),E&&k.kick()},k=N(r,a,W,{onDegrade:C}),b=e=>{const t=Math.round(u);u=t+ee(t,Q(e,s),s),k.kick()};return p.forEach((e,t)=>{r.on(e,"click",o=>{t!==v()&&(o.preventDefault(),b(t))})}),a.querySelectorAll("[data-gem-orbit-step]").forEach(e=>{r.on(e,"click",()=>b(u+(Number(e.getAttribute("data-gem-orbit-step"))||0)))}),P&&r.on(P,"click",e=>{const t=P.getBoundingClientRect();t.width&&b(Math.round((e.clientX-t.left)/t.width*(s-1)))}),G(r,F,{start:()=>{I=c},move:e=>{y=!0,a.setAttribute("data-gem-state","dragging"),c=I-e/T,k.kick()},end:(e,t)=>{y&&(y=!1,a.setAttribute("data-gem-state","ready"),u=t<4?Math.round(c):ae(c,-e/T,i.carry,Math.min(3,Math.ceil(s/2))),k.kick())}}),re(r,a,e=>b(u+e)),j(r,a,{accept:e=>e.target===a||p.includes(e.target),step:e=>b(u+e),activate:e=>e.target===a?p[v()].click():!1}),D(),J(r,a,C),a.gemQuickViewHost={key:"orbit",products:A,indexOf:H,opens:e=>H(e)===v(),imageFor:e=>M[e]||null,triggerFor:e=>p[e]||null,goTo:b,setOpen:e=>a.setAttribute("data-gem-state",e?"open":"ready"),setImage:(e,t)=>Y(M[e],t),setTint:$},a.setAttribute("data-gem-quick-view-host","orbit"),a.setAttribute("data-gem-state","ready"),_(!0),i.drift&&q()&&k.kick(),()=>{delete a.gemQuickViewHost,a.removeAttribute("data-gem-quick-view-host"),a.removeAttribute("data-gem-state")}}const se=K({key:"orbit",hook:"[data-gem-orbit]",load:"viewport",renderer:"dom",tier:"static",fallback:"markup",budget:{gzip:6144},settings:{rx:{type:"number",min:.1,max:.5,step:.01,default:.36,css:!0,label:"Ring width (share of the stage width)"},tilt:{type:"number",min:0,max:.5,step:.01,default:.3,css:!0,label:"Tilt (rise of the back, share of the stage height)"},size:{type:"number",min:.2,max:.9,step:.01,default:.62,css:!0,label:"Piece size (share of the stage height)"},back:{type:"number",min:.1,max:1,step:.01,default:.42,css:!0,label:"Scale at the back"},fade:{type:"number",min:0,max:1,step:.01,default:.35,css:!0,label:"Opacity at the back"},blur:{type:"number",min:0,max:8,step:.5,default:2.5,css:!0,label:"Blur at the back (px)"},lean:{type:"number",min:0,max:20,step:1,default:5,css:!0,label:"Lean at the sides (degrees)"},spread:{type:"number",min:0,max:180,step:1,default:0,css:!0,label:"Degrees between pieces (0: round the full ring)"},start:{type:"integer",min:0,default:0,css:!0,label:"Piece in front at first"},ease:{type:"number",min:40,max:600,step:10,default:140,label:"Settle time (ms)"},carry:{type:"number",min:0,max:800,step:10,default:240,label:"How far a throw carries (ms)"},tint:{type:"boolean",default:!0,label:"Tint the band with the front piece's colour"},drift:{type:"boolean",default:!1,label:"Idle float (full tier only)"},live:{type:"text",default:"{index} / {count}: {title}, {price}",label:"What a screen reader hears when the front piece changes"}},mount:ie});export{se as orbitEffect};
@@ -0,0 +1,4 @@
1
+ // Types for @gemboss/gem-framework/motion/quick-view (quick-view.js, copied as is into dist/motion/).
2
+ import type { GemMotionEffect } from "./core.js";
3
+
4
+ export declare const quickViewEffect: GemMotionEffect;
@@ -0,0 +1 @@
1
+ import{defineEffect as Ce,flip as we,loadImage as ze,priceText as oe,putImage as ie,readProduct as xe,swapText as _e}from"./core.js";const H="cubic-bezier(.2,.75,.15,1)",be={add:"Add to bag",adding:"Adding\u2026",added:"Added",added_status:"added to bag",failed:"Could not add to bag",choose:"Choose an option",sold_out:"Sold out",details:"Full details",previous:"Previous product",next:"Next product",close:"Close",colour:"Colour",size:"Size"};let ke=0;const ve=a=>String(a).padStart(2,"0"),P=a=>a==null?"":String(a);function Se(a){const s=a.closest("[data-gem-product]"),w=a.closest("[data-gem-quick-view-group]"),q=[],d=[];let l=-1;for(const o of w?w.querySelectorAll("[data-gem-product]"):s?[s]:[]){const y=xe(o);y&&(o===s&&(l=q.length),q.push(y),d.push(o))}return{index:l,host:{key:"card",products:q,imageFor:o=>d[o]?d[o].querySelector("img"):null,triggerFor:o=>d[o]?d[o].matches("[data-gem-quick-view-trigger]")?d[o]:d[o].querySelector("[data-gem-quick-view-trigger]"):null,goTo:()=>{},setOpen:()=>{}}}}function Ee(a){if(a.session.quickView)return a.session.quickView;const s={panels:new Map,pending:null};return a.session.quickView=s,a.on(a.window,"gem:cart:added",()=>s.pending&&s.pending(!0)),a.on(a.window,"gem:cart:add-failed",()=>s.pending&&s.pending(!1)),a.cleanup(()=>{s.panels.forEach(w=>w.destroy()),s.panels.clear()}),s}function Te(a,s,w){const q=a.ownerDocument,d=s.window,l=()=>s.tier()==="static",o=(e,t,n)=>{const i=q.createElement(e);i.className=t;for(const[h,c]of Object.entries(n||{}))i.setAttribute(h,c);return i};ke+=1;const y="gem-quick-view-title-"+ke,b=o("div","gem-popup-shell gem-quick-view__shell",{"data-gem-quick-view-panel":"","data-gem-popup-desktop":"split-left","data-gem-popup-mobile":"sheet","data-gem-popup-media":"top","data-gem-popup-align":"start"}),r=o("dialog","gem-popup-shell__dialog gem-quick-view__dialog",{"aria-labelledby":y}),S=o("div","gem-popup-shell__frame gem-quick-view__frame"),F=o("div","gem-quick-view__surface",{"aria-hidden":"true"}),E=o("button","gem-popup-shell__close gem-quick-view__close",{type:"button"}),se=o("div","gem-popup-shell__media gem-quick-view__media"),A=o("img","gem-quick-view__image",{alt:""}),C=o("img","gem-quick-view__image gem-quick-view__image--next",{alt:"","aria-hidden":"true"}),O=o("div","gem-popup-shell__panel gem-quick-view__panel"),Q=o("div","gem-quick-view__bar"),R=o("button","gem-quick-view__icon",{type:"button"}),V=o("button","gem-quick-view__icon",{type:"button"}),ae=o("span","gem-quick-view__count"),U=o("p","gem-quick-view__eyebrow"),re=o("h2","gem-quick-view__title",{id:y}),J=o("p","gem-quick-view__price"),M=o("s","gem-quick-view__compare"),B=o("div","gem-quick-view__swatches",{role:"radiogroup"}),T=o("div","gem-quick-view__sizes",{role:"radiogroup"}),k=o("button","gem-button gem-button--primary gem-quick-view__add",{type:"button"}),N=o("p","gem-quick-view__status",{role:"status"}),Y=o("p","gem-quick-view__description"),j=o("a","gem-quick-view__details");E.textContent="\xD7",R.textContent="\u2039",V.textContent="\u203A",se.append(A,C),Q.append(R,ae,V),O.append(Q,U,re,J,M,B,T,k,N,Y,j),S.append(E,se,O),r.append(F,S),b.append(r),a.append(b);const ce=new Map;let p=null,f=[],g=0,I=0,z=-1,m=null,le=null,D=null,u=be,$=!1,G=0;const _=()=>f[g],K=(e,t,n)=>typeof e.animate=="function"?e.animate(t,n):null,W=e=>{e.getAnimations&&e.getAnimations().forEach(t=>t.cancel())},L=e=>{m&&(e?m.setAttribute("data-gem-quick-view-source",""):m.removeAttribute("data-gem-quick-view-source"))},de=()=>{const e=p.imageFor(g);e!==m&&(L(!1),m=e,r.open&&L(!0))},ue=e=>{p.setTint?p.setTint(e):e?b.style.setProperty("--gem-motion-tint",e):b.style.removeProperty("--gem-motion-tint")},pe=(e,t)=>{if(A.alt=e.alt||P(_().title),!t||l()||typeof C.animate!="function")return ie(A,e);ie(C,e);const n=()=>{const i=()=>{ie(A,e),C.removeAttribute("src"),C.removeAttribute("srcset")},h=C.animate([{opacity:0,transform:"scale(.97)"},{opacity:1,transform:"none"}],{duration:280,easing:H});h&&h.finished?h.finished.then(i,i):i()};typeof C.decode=="function"?C.decode().then(n,n):n()},ge=()=>{const e=_(),t=e.colors&&e.colors[I],n=e.sizes&&e.sizes[z];return Array.isArray(e.variants)&&e.variants.find(i=>(!t||(i.options||[]).includes(t.name))&&(!n||(i.options||[]).includes(n.name)))||null},qe=()=>{const e=_(),t=ge();if(t)return t.available===!1?null:t.id;const n=e.colors&&e.colors[I],i=e.sizes&&e.sizes[z];return i&&i.variant_id!=null?i.variant_id:n&&n.variant_ids&&n.variant_ids.length?n.variant_ids[0]:e.variant_id!=null?e.variant_id:null},X=()=>{const e=ge();return e&&(e.price_text!=null||e.price!=null)?e:_()},me=e=>{const t=oe(X(),D);e?_e(J,t,l()):J.textContent=t,M.textContent=oe(X(),D,"compare"),M.hidden=!M.textContent},Z=()=>{const e=_(),t=e.sizes&&e.sizes[z],n=e.sold_out===!0||!!(e.sizes&&e.sizes.length)&&z<0;k.disabled=n,k.textContent=n?u.sold_out:[u.add,t?t.name:"",oe(X(),D)].filter(Boolean).join(" \xB7 ")},fe=e=>{const t=_(),n=Array.isArray(t.colors)?t.colors:[];I=Math.min(ce.get(t.id)||0,Math.max(0,n.length-1)),z=(t.sizes||[]).findIndex(c=>c.available!==!1);const i=n[I];ae.textContent=ve(g+1)+" / "+ve(f.length),Q.hidden=f.length<2;const h=(c,v)=>{e?_e(c,v,l()):c.textContent=v};h(U,P(t.eyebrow)),h(re,P(t.title)),me(e),U.hidden=!t.eyebrow,Y.textContent=P(t.description),Y.hidden=!t.description,j.textContent=u.details,t.url&&j.setAttribute("href",t.url),j.hidden=!t.url,pe(i&&i.image||t.image,e),ue(i&&i.tint||t.tint),B.replaceChildren(...n.map((c,v)=>{const x=o("button","gem-quick-view__swatch",{type:"button",role:"radio","aria-checked":String(v===I),"aria-label":c.name,title:c.name,"data-gem-quick-view-color":String(v)});return c.hex&&x.style.setProperty("--gem-quick-view-swatch",c.hex),x})),B.hidden=n.length<2,T.replaceChildren(...(t.sizes||[]).map((c,v)=>{const x=o("button","gem-quick-view__size",{type:"button",role:"radio","aria-checked":String(v===z),"data-gem-quick-view-size":String(v)});return x.textContent=c.name,c.available===!1&&(x.disabled=!0,x.setAttribute("aria-label",c.name+", "+u.sold_out)),x})),T.hidden=!(t.sizes&&t.sizes.length),N.textContent="",Z(),de();for(const c of[g-1,g+1])f.length>1&&ze(q,f[(c+f.length)%f.length].image)},ee=e=>{f.length<2||(g=(g+e+f.length)%f.length,p.goTo(g),fe(!0))},he=(e,t)=>{w.pending=null,k.disabled=!1,k.textContent=e?u.added:t,N.textContent=e?P(_().title)+": "+u.added_status:u.failed,G&&d.clearTimeout(G),G=e?d.setTimeout(Z,1600):0},ye=()=>{const e=qe();if(e==null||e===""){N.textContent=u.choose;return}const t=k.textContent;k.disabled=!0,k.textContent=u.adding,k.dispatchEvent(new d.CustomEvent("gem:cart:add",{bubbles:!0,composed:!0,detail:{variantId:String(e),quantity:1}})),d.__gemCartAddBridge?w.pending=n=>he(n,t):he(!0,t)},Ae=(e,t)=>{if(Array.from(e.children).forEach((n,i)=>n.setAttribute("aria-checked",String(i===t))),e===T)z=t;else{const n=_(),i=n.colors&&n.colors[t];if(!i)return;I=t,ce.set(n.id,t),i.image&&(pe(i.image,!0),p.setImage&&p.setImage(g,i.image,i.side)),ue(i.tint||n.tint)}me(!1),Z()},te=e=>{r.removeAttribute("data-gem-state"),W(A),r.open&&r.close(),L(!1),p.setOpen(!1),$=!1;const t=p.triggerFor(g)||le;t&&typeof t.focus=="function"&&t.focus(),s.api.emit("gem:quick-view:close",{source:"gem-runtime",reason:e,productId:_().id})},ne=e=>{if(!r.open||$)return;$=!0,de();const t=m&&m.getBoundingClientRect?m.getBoundingClientRect():null,n=l()||!t?null:we(A,t,{back:!0,duration:440,easing:"cubic-bezier(.4,0,.2,1)"});if(!n||!n.finished)return te(e);r.setAttribute("data-gem-state","closing");for(const h of[O,F,E])K(h,[{opacity:1},{opacity:0}],{duration:220,fill:"forwards"});p.setOpen(!1);const i=()=>{[O,F,E].forEach(W),te(e)};n.finished.then(i,i)};s.on(E,"click",()=>ne("close-control")),s.on(R,"click",()=>ee(-1)),s.on(V,"click",()=>ee(1)),s.on(k,"click",ye);for(const[e,t]of[[B,"data-gem-quick-view-color"],[T,"data-gem-quick-view-size"]])s.on(e,"click",n=>{const i=n.target.closest&&n.target.closest(`[${t}]`);i&&!i.disabled&&Ae(e,Number(i.getAttribute(t)))});return s.on(r,"cancel",e=>{e.preventDefault(),ne("escape")}),s.on(r,"click",e=>{e.target===r&&ne("backdrop")}),s.on(r,"keydown",e=>{e.target.closest&&e.target.closest(".gem-quick-view__swatches, .gem-quick-view__sizes")||(e.key==="ArrowRight"||e.key==="ArrowLeft")&&ee(e.key==="ArrowRight"?1:-1)}),{shell:b,open(e,t,n,i){if(r.open)return;W(A),p=e,f=p.products,g=t,u=i,le=n,D=n,m=null,E.setAttribute("aria-label",u.close),R.setAttribute("aria-label",u.previous),V.setAttribute("aria-label",u.next),B.setAttribute("aria-label",u.colour),T.setAttribute("aria-label",u.size),fe(!1),m=p.imageFor(g);const h=m&&m.getBoundingClientRect?m.getBoundingClientRect():null;r.showModal(),r.setAttribute("data-gem-state","open"),p.setOpen(!0),L(!0),s.api.emit("gem:quick-view:open",{source:"gem-runtime",productId:_().id,productUrl:_().url||null,index:g,count:f.length,host:p.key}),!l()&&(we(A,h,{duration:560,easing:H}),K(F,[{opacity:0,transform:"scale(.97)"},{opacity:1,transform:"none"}],{duration:360,easing:H}),Array.from(O.children).forEach((c,v)=>K(c,[{opacity:0,transform:"translateY(12px)"},{opacity:1,transform:"none"}],{duration:380,delay:120+v*40,easing:H,fill:"backwards"})))},destroy(){r.open&&te("unmount"),b.remove()}}}function Ie(a,s){const w=Ee(s);s.on(a,"click",q=>{if(q.defaultPrevented)return;const d=a.closest("[data-gem-quick-view-host]"),l=d&&d.gemQuickViewHost;if(l&&!l.opens(a))return;const o=l?null:Se(a),y=l?l.indexOf(a):o.index;if(y<0||!(l||o.host).products[y])return;q.preventDefault();const b=l&&d||a.closest("[data-gem-quick-view-group]")||a.closest(".gem-section, [data-gem-section-id]")||a.parentElement;let r=w.panels.get(b);(!r||!r.shell.isConnected)&&w.panels.set(b,r=Te(b,s,w));const S=s.reread().labels;r.open(l||o.host,y,a,{...be,...S&&typeof S=="object"?S:{}})})}const Be=Ce({key:"quick-view",hook:"[data-gem-quick-view-trigger]",load:"interaction",renderer:"dom",tier:"static",fallback:"markup",budget:{gzip:6144},settings:{labels:{type:"json",default:{},inherit:!0,label:"The panel's words, JSON on the trigger or an ancestor: add, adding, added, added_status, failed, choose, sold_out, details, previous, next, close, colour, size"}},mount:Ie});export{Be as quickViewEffect};
@@ -0,0 +1,5 @@
1
+ // Types for @gemboss/gem-framework/motion/rail (rail.js, copied as is into dist/motion/).
2
+ import type { GemMotionEffect, GemMotionImage } from "./core.js";
3
+
4
+ export declare const railEffect: GemMotionEffect;
5
+ export declare function createTurn(el: Element | null): { mode: "pair" | "sprite" | "flip"; image: Element; set(t: number): void; setImages(image: GemMotionImage, side?: GemMotionImage): void } | null;
@@ -0,0 +1 @@
1
+ import{clamp as A,defineEffect as K,follow as U,frameTask as Z,lerp as I,observeSize as tt,priceText as et,putImage as N,readProduct as rt,setStyle as M,smoothstep as V,spring as st}from"./core.js";const ot=700,E=14;function nt(s){if(!s)return null;const i=s.querySelector(".gem-turn__side"),n=s.querySelector(".gem-turn__front"),p=s.querySelectorAll(".gem-turn__sheet"),m=Math.floor(Number(s.getAttribute("data-gem-turn-frames")))||0,y=Math.floor(Number(s.getAttribute("data-gem-turn-cols")))||0,c=p.length>1&&m>1&&y>0?"sprite":i&&n?"pair":n?"flip":null;if(!c)return null;const b=Math.ceil(m/Math.max(1,y))||1,d=(k,l)=>M(k,"transform",`translate(${(-(l%y)*100/y).toFixed(3)}%, ${(-Math.floor(l/y)*100/b).toFixed(3)}%)`);let h=-1;return{mode:c,image:c==="sprite"?s:n,set(k){const l=Math.round(k*1e3)/1e3;if(l!==h)if(h=l,c==="pair")M(i,"transform",`perspective(900px) rotateY(${(-82*l).toFixed(1)}deg)`),M(i,"opacity",(1-V(.42,.78,l)).toFixed(3)),M(n,"opacity",V(.45,.82,l).toFixed(3)),M(n,"transform",`scale(${I(.9,1,l).toFixed(3)})`);else if(c==="sprite"){const w=l*(m-1),u=Math.floor(w);d(p[0],u),d(p[1],Math.min(m-1,u+1)),M(p[1],"opacity",(w-u).toFixed(3))}else M(n,"transform",`perspective(900px) rotateY(${I(-74,0,l).toFixed(1)}deg) scale(${I(.86,1,l).toFixed(3)})`)},setImages(k,l){c!=="sprite"&&(N(n,k),N(i,l))}}}function at(s,i){const n=[],p=[],m=[],y=[],c=[];s.querySelectorAll("[data-gem-rail-item]").forEach(t=>{const e=rt(t);if(!e)return;const o=t.querySelector(".gem-rail__hit")||t;n.push(t),p.push(e),m.push(o),y.push(t.querySelector(".gem-rail__body")||o),c.push(nt(t.querySelector(".gem-turn")))});const b=n.length;if(!b)return!1;const d=s.querySelector(".gem-rail__stage")||s,h=s.querySelector(".gem-rail__track")||d,k=s.querySelector(".gem-rail__caption"),l=s.querySelector(".gem-rail__rod path"),w=i.renderer.style,u=n.map(()=>0),L=n.map(()=>({x:0,v:0}));let x=i.settings,S=n.map(()=>0),F=-1,g=!1,O=!1,P=!1,v=0,W=0,R=0,C=0,B=0;const T=()=>i.tier()==="static",D=()=>`M0 ${E} Q50 ${Math.round(E*10+x.sag*2e3)/10} 100 ${E}`;l&&l.getAttribute("d")!==D()&&l.setAttribute("d",D());const z=()=>{x=i.reread();const t=g;g=s.clientWidth<ot,v=d.clientWidth,W=d.clientHeight,g&&!t&&n.forEach(e=>w(e,"transform",""))},j=t=>{const e=A(t/Math.max(1,v),0,1);return W*E/100+4*x.sag*W*e*(1-e)},H=()=>{const t=u.map(r=>1+x.grow*r*r),e=v*.92/t.reduce((r,a)=>r+a,0);let o=v*.04;return t.map(r=>{const a=o+r*e/2;return o+=r*e,{c:a,width:r*e}})},Q=t=>{let e=-1,o=1/0;return H().forEach(({c:r,width:a},f)=>{const q=Math.abs(r-t);q<o&&q<a*1.2&&(e=f,o=q)}),e},X=t=>{x.tint&&(t?s.style.setProperty("--gem-motion-tint",t):s.style.removeProperty("--gem-motion-tint"))},_=(t,e)=>{if(t===F)return;F=t,S=S.map((r,a)=>a===t?1:0);const o=p[t];if(e){u[t]=1,c[t]&&c[t].set(1);return}k&&(k.textContent=o?[o.title,et(o,s)].filter(Boolean).join(" \xB7 "):""),o&&(X(o.tint),i.emit("gem:motion:front",{index:t,product:o})),$.kick()},G=t=>{const e=U(t,x.ease);let o=!1;if(g&&O){const r=h.scrollLeft+h.clientWidth/2;let a=0;n.forEach((f,q)=>{Math.abs(f.offsetLeft+f.offsetWidth/2-r)<Math.abs(n[a].offsetLeft+n[a].offsetWidth/2-r)&&(a=q)}),a!==F&&_(a)}for(let r=0;r<b;r++){const a=S[r]-u[r];u[r]=T()||Math.abs(a)<.002?S[r]:u[r]+a*e,u[r]!==S[r]&&(o=!0);const f=L[r];T()?f.x=f.v=0:(st(f,0,t,.02,.09),f.x=A(f.x,-9,9),Math.abs(f.x)>.02||Math.abs(f.v)>.02?o=!0:f.x=f.v=0),c[r]&&c[r].set(u[r]),w(y[r],"transform",`rotate(${f.x.toFixed(2)}deg)`)}return g||H().forEach(({c:r},a)=>{w(n[a],"transform",`translate3d(${r.toFixed(1)}px, ${j(r).toFixed(1)}px, 0)`),w(n[a],"z-index",String(Math.round(10+u[a]*10)))}),P=!0,o},Y=()=>{z(),P&&$.kick()},$=Z(i,s,G,{onDegrade:Y});m.forEach((t,e)=>i.on(t,"focus",()=>_(e))),i.on(d,"click",t=>{if(g||m.some(o=>o.contains(t.target)))return;const e=Q(t.clientX-d.getBoundingClientRect().left);e>=0&&m[e].click()}),i.on(d,"pointermove",t=>{if(g||t.pointerType!=="mouse")return;const e=t.clientX-d.getBoundingClientRect().left;B=A((e-R)/Math.max(1,t.timeStamp-C),-3,3),R=e,C=t.timeStamp,!T()&&x.swing&&H().forEach(({c:o,width:r},a)=>{L[a].v+=B*.35*x.swing*A(1-Math.abs(o-e)/(r*2.5),0,1)}),_(Q(e)),$.kick()}),i.on(d,"pointerleave",()=>{g||_(-1)}),i.on(h,"keydown",t=>{t.key!=="ArrowRight"&&t.key!=="ArrowLeft"||(t.preventDefault(),m[A(Math.max(0,F)+(t.key==="ArrowRight"?1:-1),0,b-1)].focus())}),i.on(h,"scroll",()=>{g&&(O=!0,$.kick())},{passive:!0}),z(),tt(i,s,Y),g&&_(0,!0);const J=t=>{const e=(t%b+b)%b;g&&typeof h.scrollTo=="function"&&h.scrollTo({left:n[e].offsetLeft-(h.clientWidth-n[e].offsetWidth)/2,behavior:T()?"auto":"smooth"}),_(e)};return s.gemQuickViewHost={key:"rail",products:p,indexOf:t=>n.findIndex(e=>e===t||e.contains(t)),opens:()=>!0,imageFor:t=>(c[t]?c[t].image:n[t]&&n[t].querySelector("img"))||null,triggerFor:t=>m[t]||null,goTo:J,setOpen:t=>s.setAttribute("data-gem-state",t?"open":"ready"),setImage:(t,e,o)=>c[t]&&c[t].setImages(e,o),setTint:X},s.setAttribute("data-gem-quick-view-host","rail"),s.setAttribute("data-gem-state","ready"),()=>{delete s.gemQuickViewHost,s.removeAttribute("data-gem-quick-view-host"),s.removeAttribute("data-gem-state")}}const lt=K({key:"rail",hook:"[data-gem-rail]",load:"viewport",renderer:"dom",tier:"static",fallback:"markup",budget:{gzip:6144},settings:{sag:{type:"number",min:0,max:.2,step:.01,default:.07,css:!0,label:"How far the rod dips (share of the stage height)"},height:{type:"number",min:.3,max:.85,step:.01,default:.7,css:!0,label:"Garment height (share of the stage height)"},grow:{type:"number",min:0,max:3,step:.1,default:1.5,label:"How much wider a turned piece gets"},swing:{type:"number",min:0,max:2,step:.1,default:1,label:"Hanger swing (0: off)"},ease:{type:"number",min:40,max:600,step:10,default:120,label:"Turn time (ms)"},tint:{type:"boolean",default:!0,label:"Tint the band with the turned piece's colour"}},mount:at});export{nt as createTurn,lt as railEffect};
@@ -0,0 +1,39 @@
1
+ .gem-orbit { display: block; position: relative; background-color: var(--gem-motion-tint, transparent); }
2
+ .gem-orbit:focus-visible { outline: var(--gem-focus-ring); outline-offset: calc(-1 * var(--gem-focus-offset)); }
3
+ .gem-orbit__stage { position: relative; height: var(--gem-orbit-stage-height, clamp(340px, 58vh, 620px)); overflow: hidden; container-type: size; }
4
+ .gem-orbit__ring { list-style: none; margin: 0; padding: 0; position: absolute; left: 50%; top: 66%; width: 0; height: 0; }
5
+ .gem-orbit__item {
6
+ --gem-orbit-k: calc(mod(var(--gem-orbit-i, 0) - var(--gem-orbit-start, 0) + var(--gem-orbit-n, 1) / 2, var(--gem-orbit-n, 1)) - var(--gem-orbit-n, 1) / 2);
7
+ --gem-orbit-a: calc(var(--gem-orbit-k) * var(--gem-orbit-spread, calc(360 / var(--gem-orbit-n, 1))) * 1deg);
8
+ --gem-orbit-d: calc((1 - cos(var(--gem-orbit-a))) / 2);
9
+ position: absolute; left: calc(var(--gem-orbit-size, 0.62) * -50cqh); top: calc(var(--gem-orbit-size, 0.62) * -50cqh);
10
+ width: calc(var(--gem-orbit-size, 0.62) * 100cqh); height: calc(var(--gem-orbit-size, 0.62) * 100cqh);
11
+ transform: translate3d(calc(var(--gem-orbit-rx, 0.36) * 100cqw * sin(var(--gem-orbit-a))), calc(var(--gem-orbit-tilt, 0.3) * -100cqh * var(--gem-orbit-d)), 0)
12
+ scale(calc(1 + (var(--gem-orbit-back, 0.42) - 1) * var(--gem-orbit-d))) rotate(calc(sin(var(--gem-orbit-a)) * var(--gem-orbit-lean, 5) * 1deg));
13
+ opacity: calc(1 + (var(--gem-orbit-fade, 0.35) - 1) * var(--gem-orbit-d));
14
+ z-index: calc((1 - var(--gem-orbit-d)) * 100);
15
+ filter: blur(calc(var(--gem-orbit-blur, 2.5) * var(--gem-orbit-d) * 1px));
16
+ }
17
+ .gem-orbit__item::after {
18
+ content: ''; position: absolute; left: 18%; right: 18%; bottom: -2%; height: 7%; z-index: -1; border-radius: var(--gem-radius-pill);
19
+ background: radial-gradient(closest-side, color-mix(in srgb, var(--gem-color-scrim, #000) 18%, transparent), transparent);
20
+ }
21
+ .gem-orbit__hit { display: block; width: 100%; height: 100%; padding: 0; border: 0; color: inherit; background: transparent; text-decoration: none; cursor: pointer; }
22
+ .gem-orbit__hit:focus-visible { outline: var(--gem-focus-ring); outline-offset: var(--gem-focus-offset); border-radius: var(--gem-radius-md); }
23
+ .gem-orbit__img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
24
+ .gem-orbit__caption { display: grid; justify-items: center; gap: var(--gem-space-2xs); margin: calc(-1 * var(--gem-space-xs)) 0 0; text-align: center; }
25
+ .gem-orbit__name { margin: 0; font-family: var(--gem-font-heading); font-size: var(--gem-h4); font-weight: 600; line-height: 1.2; }
26
+ .gem-orbit__price { margin: 0; color: var(--gem-color-muted); font-variant-numeric: tabular-nums; }
27
+ .gem-orbit__dock { display: flex; align-items: center; justify-content: center; gap: var(--gem-space-sm); padding: var(--gem-space-md) 0 var(--gem-space-xs); font-size: var(--gem-text-sm); font-variant-numeric: tabular-nums; }
28
+ .gem-orbit__arrow {
29
+ display: inline-grid; place-items: center; width: var(--gem-tap-min); height: var(--gem-tap-min); padding: 0; cursor: pointer;
30
+ border: var(--gem-stroke, 1px) solid currentColor; border-radius: var(--gem-radius-pill); color: inherit; background: transparent; font: inherit; line-height: 1;
31
+ }
32
+ .gem-orbit__arrow:hover { color: var(--gem-color-background); background: var(--gem-color-text); border-color: var(--gem-color-text); }
33
+ .gem-orbit__arrow:focus-visible { outline: var(--gem-focus-ring); outline-offset: var(--gem-focus-offset); }
34
+ .gem-orbit__count { min-width: 7ch; text-align: center; font-variant-numeric: tabular-nums; }
35
+ .gem-orbit__live { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
36
+ .gem-orbit__track { position: relative; width: min(180px, 30vw); height: var(--gem-stroke-thick, 2px); background: color-mix(in srgb, currentColor 18%, transparent); }
37
+ .gem-orbit__track::before { content: ''; position: absolute; inset: calc(-1 * var(--gem-space-sm)) 0; }
38
+ .gem-orbit__bar { position: absolute; inset: 0; background: currentColor; transform-origin: left; transform: scaleX(calc((var(--gem-orbit-start, 0) + 1) / var(--gem-orbit-n, 1))); }
39
+ @media (pointer: coarse), (prefers-reduced-motion: reduce) { .gem-orbit__item { filter: none; } }
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "before_after";
6
6
  export const readyEvent = "gem:before-after: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('before_after', ({ api }) => {
10
10
  const frames = document.querySelectorAll('[data-gem-before-after-frame], .gem-before-after__frame');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "bundle";
6
6
  export const readyEvent = "gem:bundle: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('bundle', ({ api }) => {
10
10
  let boundBundles = 0;
11
11
  const BB_WORDS = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "carousel";
6
6
  export const readyEvent = "gem:carousel: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('carousel', ({ api }) => {
10
10
  const carousels = document.querySelectorAll('[data-gem-carousel], .gem-carousel');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "cart-drawer";
6
6
  export const readyEvent = "gem:cart: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('cart-drawer', ({ api }) => {
10
10
  const drawers = document.querySelectorAll('[data-gem-cart-drawer], [data-gem-cart-canvas], .gem-cart-drawer');
11
11
  const cartLinks = document.querySelectorAll('[data-gem-cart-drawer-trigger], [data-gem-cart-toggle], a[href*="/cart"]');
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "context-adapter";
6
6
  export const readyEvent = "gem:context: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("context-adapter", "gem:context:ready");
10
10
  }
11
11
 
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "countdown";
6
6
  export const readyEvent = "gem:countdown: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('countdown', ({ api }) => {
10
10
  const timers = document.querySelectorAll('[data-gem-countdown], .gem-countdown');
11
11
  let bound = 0;
@@ -0,0 +1,9 @@
1
+ // @gemboss/gem-framework/plugins/delivery-estimate: 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: "delivery-estimate";
6
+ export declare const readyEvent: "gem:delivery-estimate:ready";
7
+ /** Bind every "delivery-estimate" 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{mountDeliveryEstimate as g}from"../pod/delivery-estimate.js";const f="delivery-estimate",N="gem:delivery-estimate:ready";function p({document:e,window:t,target:x,mark:i,canListen:n,listen:m,registerPlugin:s,registerNoopPlugin:E,onUnmount:u}){s("delivery-estimate",({api:o})=>{const a=g({window:t,document:e,on:(r,l,y,c)=>{n(r)&&m(r,l,y,c)},mark:i,api:o},u);o.emit("gem:delivery-estimate:ready",{source:"gem-runtime",count:a})})}function v(e,t){return d(f,p,e,t)}var b=v;export{b as default,f as key,v as mount,N as readyEvent};
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "disclosure";
6
6
  export const readyEvent = "gem:disclosure: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('disclosure', ({ api }) => {
10
10
  const items = document.querySelectorAll('details[data-gem-disclosure], .gem-accordion details, .gem-tabs details, .gem-footer-shell details, [data-gem-footer-shell] details');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "hero-slideshow";
6
6
  export const readyEvent = "gem:hero-slideshow: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('hero-slideshow', ({ api }) => {
10
10
  const roots = document.querySelectorAll('[data-gem-hero-slideshow]');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "img-in";
6
6
  export const readyEvent = "gem:img-in: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('img-in', ({ api }) => {
10
10
  const STATE = 'data-gem-img-in-state';
11
11
  const BOUND = 'data-gem-bound-img-in';
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "localization";
6
6
  export const readyEvent = "gem:localization: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('localization', ({ api }) => {
10
10
  const forms = document.querySelectorAll('form[action*="/localization"], form[action*="localization"], [data-gem-footer-localization], [data-gem-header-localization], .gem-navigation-shell__localization, .gem-shell-footer__localization, .gem-shell-header__localization-form');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "media-gallery";
6
6
  export const readyEvent = "gem:media: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('media-gallery', ({ api }) => {
10
10
  const galleries = document.querySelectorAll('[data-gem-media-gallery], .gem-media-gallery, .gem-product-media-gallery');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "mobile-menu";
6
6
  export const readyEvent = "gem:menu: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('mobile-menu', ({ api }) => {
10
10
  const menus = document.querySelectorAll('[data-gem-mobile-menu], details.gem-shell-header__drawer');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "offer-engine";
6
6
  export const readyEvent = "gem:offer: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('offer-engine', ({ api }) => {
10
10
  (function(){
11
11
  document.querySelectorAll('[data-gem-offer-engine]').forEach(function(root){
@@ -0,0 +1,9 @@
1
+ // @gemboss/gem-framework/plugins/orbit: 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: "orbit";
6
+ export declare const readyEvent: "gem:orbit:ready";
7
+ /** Bind every "orbit" 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 f}from"../runtime-core.js";import{mountEffects as _}from"../motion/core.js";import{orbitEffect as d}from"../motion/orbit.js";const l="orbit",P="gem:orbit:ready";function p({document:t,window:e,target:y,mark:a,canListen:i,listen:m,registerPlugin:s,registerNoopPlugin:w,onUnmount:g}){s("orbit",({api:o})=>{const n=_(d,{window:e,document:t,on:(r,b,c,u)=>{i(r)&&m(r,b,c,u)},mark:a,api:o,css:".gem-orbit{--gem-orbit-css:1;outline:none;user-select:none;-webkit-user-select:none;transition:background-color var(--gem-motion-slow,0.6s) var(--gem-ease-soft,ease)}.gem-orbit[data-gem-state] .gem-orbit__stage{touch-action:pan-y;cursor:grab}.gem-orbit[data-gem-state='dragging'] .gem-orbit__stage{cursor:grabbing}.gem-orbit[data-gem-state] .gem-orbit__item{will-change:transform}.gem-orbit[data-gem-state] .gem-orbit__hit{transition:opacity var(--gem-motion-base,0.35s) var(--gem-ease-soft,ease),transform var(--gem-motion-base,0.35s) var(--gem-ease-soft,ease)}.gem-orbit[data-gem-state] .gem-orbit__bar{transition:transform var(--gem-motion-slow,0.4s) var(--gem-ease-soft,ease)}.gem-orbit[data-gem-state] .gem-orbit__arrow{transition:color var(--gem-motion-fast,0.2s) ease,background-color var(--gem-motion-fast,0.2s) ease}.gem-orbit[data-gem-state='open'] .gem-orbit__item:not([aria-current='true']) .gem-orbit__hit{opacity:0;transform:scale(0.92)}@media (prefers-reduced-motion:reduce){.gem-orbit,.gem-orbit[data-gem-state] .gem-orbit__hit,.gem-orbit[data-gem-state] .gem-orbit__bar,.gem-orbit[data-gem-state] .gem-orbit__arrow{transition:none}}"},g);o.emit("gem:orbit:ready",{source:"gem-runtime",count:n})})}function v(t,e){return f(l,p,t,e)}var N=v;export{N as default,l as key,v as mount,P as readyEvent};
@@ -0,0 +1,9 @@
1
+ // @gemboss/gem-framework/plugins/personalize: 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: "personalize";
6
+ export declare const readyEvent: "gem:personalize:ready";
7
+ /** Bind every "personalize" 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{mountPersonalize as u}from"../pod/personalize.js";const z="personalize",w="gem:personalize:ready";function _({document:e,window:o,target:v,mark:t,canListen:n,listen:i,registerPlugin:l,registerNoopPlugin:x,onUnmount:p}){l("personalize",({api:a})=>{const s=u({window:o,document:e,on:(r,m,g,c)=>{n(r)&&i(r,m,g,c)},mark:t,api:a,css:'.gem-el-product-personalization[data-gem-personalize-invalid] .gem-el-product-personalization__input,.gem-el-product-personalization[data-gem-personalize-invalid] .gem-el-product-personalization__upload,.gem-el-product-personalization[data-gem-personalize-invalid] .gem-el-product-personalization__swatch,.gem-el-product-personalization[data-gem-personalize-invalid] .gem-el-product-personalization__choice-image{border-color:var(--gem-color-danger)}.gem-el-product-personalization[data-gem-personalize-state="uploading"] .gem-el-product-personalization__upload{cursor:progress;opacity:0.7}.gem-el-product-personalization__count[data-gem-personalize-full]{color:var(--gem-color-text);font-weight:600}'},p);a.emit("gem:personalize:ready",{source:"gem-runtime",count:s})})}function f(e,o){return d(z,_,e,o)}var b=f;export{b as default,z as key,f as mount,w as readyEvent};
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "predictive-search";
6
6
  export const readyEvent = "gem:search: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('predictive-search', ({ api }) => {
10
10
  const forms = document.querySelectorAll('[data-gem-predictive-search], [data-gem-search-canvas], form[action*="/search"], form[role="search"]');
11
11
  const toggles = document.querySelectorAll('[data-gem-search-toggle]');
@@ -0,0 +1,9 @@
1
+ // @gemboss/gem-framework/plugins/print-preview: 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: "print-preview";
6
+ export declare const readyEvent: "gem:print-preview:ready";
7
+ /** Bind every "print-preview" 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 w}from"../runtime-core.js";import{mountPrintPreview as d}from"../pod/print-preview.js";const s="print-preview",k="gem:print-preview:ready";function u({document:e,window:t,target:h,mark:n,canListen:p,listen:o,registerPlugin:a,registerNoopPlugin:x,onUnmount:g}){a("print-preview",({api:r})=>{const l=d({window:t,document:e,on:(i,m,c,v)=>{p(i)&&o(i,m,c,v)},mark:n,api:r,css:'.gem-el-product-print-preview__text{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--gem-print-preview-lines,2);max-width:100%;margin:0;overflow:hidden;overflow-wrap:anywhere;font-family:var(--gem-print-preview-font,var(--gem-font-heading));font-size:calc(82cqh / var(--gem-print-preview-lines,2));line-height:1.1;color:var(--gem-print-preview-color,var(--gem-color-text));text-align:center;transform-origin:center}.gem-el-product-print-preview[data-gem-print-preview-align="left"] .gem-el-product-print-preview__area{place-items:center start}.gem-el-product-print-preview[data-gem-print-preview-align="left"] .gem-el-product-print-preview__text{text-align:start;transform-origin:left center}.gem-el-product-print-preview[data-gem-print-preview-align="right"] .gem-el-product-print-preview__area{place-items:center end}.gem-el-product-print-preview[data-gem-print-preview-align="right"] .gem-el-product-print-preview__text{text-align:end;transform-origin:right center}.gem-el-product-print-preview__photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}'},g);r.emit("gem:print-preview:ready",{source:"gem-runtime",count:l})})}function f(e,t){return w(s,u,e,t)}var P=f;export{P as default,s as key,f as mount,k as readyEvent};
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "promo-popup";
6
6
  export const readyEvent = "gem:promo-popup: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('promo-popup', ({ api }) => {
10
10
  const popups = document.querySelectorAll('[data-gem-promo-popup]');
11
11
  let bound = 0;
@@ -5,7 +5,7 @@ import { mountPlugin } from "../runtime-core.js";
5
5
  export const key = "quantity-input";
6
6
  export const readyEvent = "gem:quantity: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('quantity-input', ({ api }) => {
10
10
  const controls = document.querySelectorAll('[data-gem-quantity-input], .gem-quantity-input');
11
11
  let bound = 0;