@momentco-ai/moment-sdk 0.2.0-dev.14 → 0.2.1-dev.16

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 CHANGED
@@ -129,7 +129,7 @@ Programmatically create a styled trigger button:
129
129
  </script>
130
130
  ```
131
131
 
132
- The button renders with Moment's default styling (stone-900 background, subtle border, rounded corners). Override with CSS or pass a `className` option.
132
+ The button uses the same default `.moment-sync-trigger` styling as declarative HTML triggers. Override with CSS or pass a `className` option.
133
133
 
134
134
  ### Styling, fonts, and labels
135
135
 
@@ -171,7 +171,7 @@ You can customize trigger button appearance in a few ways:
171
171
  </script>
172
172
  ```
173
173
 
174
- You can also create plain HTML triggers and fully own styles/text:
174
+ You can also create plain HTML triggers the SDK applies minimal default button styles automatically, or you can fully own styles/text with your own CSS classes:
175
175
 
176
176
  ```html
177
177
  <button
@@ -1 +1,25 @@
1
- (function(h){"use strict";class y{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),r=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${r},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=s=>{if(s.origin!==this.opts.liveOrigin||s.source!==this.popup)return;const n=s.data;!n||typeof n!="object"||n.source!=="moment-live-embed"||n.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(n,this.opts.liveOrigin),this.opts.onResult(n),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}const w=".moment-sync-trigger",T="https://live.dev.momentco.ai";class g{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:w,...e},this.liveBaseUrl=T;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){this.emitAnalytics("embed.open",{teamSlug:e.teamSlug,triggerType:e.triggerType}),this.createModal(e)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=s=>{s.preventDefault(),this.handleTriggerClick(t)},r=this.boundClickHandlers.get(t);r&&t.removeEventListener("click",r),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,r=e.getAttribute("data-moment-list-slug")??void 0,s=e.getAttribute("data-moment-trigger-type");let n="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"?n=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const d=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",u=d?d.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let m;if(a!=null&&a!==""&&(a==="google"||a==="outlook"?m=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a)),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const l=n==="team"?void 0:i,c=n==="team"?void 0:r,p=n==="team"?[]:u;this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:n,momentIds:p}),this.open({teamSlug:t,momentSlug:l,listSlug:c,triggerType:n,ids:p,calendar:m??void 0})}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const r=document.createElement("div");Object.assign(r.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const s=document.createElement("div");s.setAttribute("role","dialog"),s.setAttribute("aria-modal","true"),s.setAttribute("aria-label","Calendar sync modal"),s.tabIndex=-1,Object.assign(s.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const n=document.createElement("button");n.type="button",n.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',n.setAttribute("aria-label","Close"),Object.assign(n.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),n.addEventListener("mouseenter",()=>{n.style.color="#666"}),n.addEventListener("mouseleave",()=>{n.style.color="#999"}),n.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),s.appendChild(n),s.appendChild(this.iframe);const d=document.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const u=document.createElement("span");u.textContent="Powered by",Object.assign(u.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),d.appendChild(u),d.appendChild(a),r.appendChild(s),r.appendChild(d),this.overlay.appendChild(r),document.body.appendChild(this.overlay);const m=l=>{if(l.key!=="Tab")return;const c=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(c.length===0){l.preventDefault(),s.focus();return}const p=c[0],v=c[c.length-1],b=document.activeElement;l.shiftKey&&b===p?(l.preventDefault(),v.focus()):!l.shiftKey&&b===v&&(l.preventDefault(),p.focus())};s.addEventListener("keydown",m),n.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=m}buildIframeUrl(e){const t=new URLSearchParams;return t.set("teamSlug",e.teamSlug),e.momentSlug&&t.set("momentSlug",e.momentSlug),e.listSlug&&t.set("listSlug",e.listSlug),t.set("triggerType",e.triggerType),e.ids?.length&&t.set("ids",e.ids.join(",")),e.calendar&&t.set("calendar",e.calendar),t.set("returnOrigin",window.location.origin),t.set("embed","1"),`${this.liveBaseUrl}/en/embed/sync?${t.toString()}`}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const r=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,r)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new y({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function f(o){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),o.className&&o.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),Object.assign(e.style,{backgroundColor:"#1c1917",color:"#ffffff",border:"1px solid #292524",borderRadius:"4px",padding:"8px 16px",fontSize:"14px",fontWeight:"500",cursor:"pointer",lineHeight:"1.4"}),e.setAttribute("data-moment-team-slug",o.teamSlug),o.momentSlug&&e.setAttribute("data-moment-slug",o.momentSlug),o.listSlug&&e.setAttribute("data-moment-list-slug",o.listSlug),o.triggerType&&e.setAttribute("data-moment-trigger-type",o.triggerType),o.ids?.length&&e.setAttribute("data-moment-ids",o.ids.join(",")),o.calendar&&e.setAttribute("data-moment-calendar",o.calendar),e.textContent=o.label??"Add to Calendar",e}if(typeof window<"u"){const o=window;o.MomentSdk=g,o.createMomentButton=f}h.MomentSdk=g,h.createMomentButton=f,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
1
+ (function(h){"use strict";class w{popup=null;pollTimer=null;messageHandler=null;resultReceived=!1;opts;constructor(e){this.opts=e}open(e){this.cleanup(),this.resultReceived=!1;const t=this.buildPopupUrl(e),i=Math.round(window.screenX+(window.outerWidth-500)/2),r=Math.round(window.screenY+(window.outerHeight-700)/2);if(this.popup=window.open(t,"moment-oauth-popup",`width=500,height=700,left=${i},top=${r},menubar=no,toolbar=no,location=yes,status=no`),!this.popup||this.popup.closed){this.sendStatusToIframe("popup-blocked"),this.opts.onPopupBlocked();return}this.sendStatusToIframe("popup-opened"),this.startPolling(),this.messageHandler=s=>{if(s.origin!==this.opts.liveOrigin||s.source!==this.popup)return;const n=s.data;!n||typeof n!="object"||n.source!=="moment-live-embed"||n.type!=="moment.embed.result"||(this.resultReceived=!0,this.sendStatusToIframe("completed"),this.opts.iframeWindow&&this.opts.iframeWindow.postMessage(n,this.opts.liveOrigin),this.opts.onResult(n),this.cleanup())},window.addEventListener("message",this.messageHandler)}cleanup(){if(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.popup&&!this.popup.closed)try{this.popup.close()}catch{}this.popup=null}buildPopupUrl(e){const t=window.location.origin,i=new URLSearchParams({provider:e.provider,teamSlug:e.teamSlug,ids:e.ids.join(","),triggerType:e.triggerType,returnOrigin:t});return e.subscriptionType&&i.set("subscriptionType",e.subscriptionType),`${this.opts.liveBaseUrl}/en/embed/oauth-popup?${i.toString()}`}startPolling(){this.pollTimer=setInterval(()=>{(!this.popup||this.popup.closed)&&(this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.resultReceived||(this.sendStatusToIframe("popup-closed"),this.opts.onPopupClosed()),this.cleanup())},500)}sendStatusToIframe(e){if(!this.opts.iframeWindow)return;const t={source:"moment-sdk",type:"moment.embed.oauth.status",status:e};this.opts.iframeWindow.postMessage(t,this.opts.liveOrigin)}}const g="moment-sdk-trigger-styles",T=`
2
+ :where(.moment-sync-trigger) {
3
+ background-color: #1c1917;
4
+ color: #ffffff;
5
+ border: 1px solid #292524;
6
+ border-radius: 4px;
7
+ padding: 8px 16px;
8
+ font-size: 14px;
9
+ font-weight: 500;
10
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
11
+ line-height: 1.4;
12
+ cursor: pointer;
13
+ appearance: none;
14
+ -webkit-appearance: none;
15
+ }
16
+
17
+ :where(.moment-sync-trigger:hover) {
18
+ background-color: #292524;
19
+ }
20
+
21
+ :where(.moment-sync-trigger:focus-visible) {
22
+ outline: 2px solid #1c1917;
23
+ outline-offset: 2px;
24
+ }
25
+ `;function S(){if(typeof document>"u"||document.getElementById(g))return;const o=document.createElement("style");o.id=g,o.textContent=T,document.head.appendChild(o)}const E=".moment-sync-trigger",k="https://live.dev.momentco.ai";class f{opts;liveBaseUrl;liveOrigin;overlay=null;iframe=null;popupBridge=null;messageHandler=null;boundClickHandlers=new Map;activePayload=null;previousFocusedElement=null;previousBodyOverflow="";didManageBodyOverflow=!1;closeTimer=null;constructor(e){this.opts={triggerSelector:E,...e},this.liveBaseUrl=k;try{this.liveOrigin=new URL(this.liveBaseUrl).origin}catch{throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`)}S(),this.bindTriggers(),this.emitAnalytics("embed.init")}open(e){this.emitAnalytics("embed.open",{teamSlug:e.teamSlug,triggerType:e.triggerType}),this.createModal(e)}close(){this.handleCancel()}rebind(){this.unbindTriggers(),this.bindTriggers()}destroy(){this.cleanup(),this.unbindTriggers()}bindTriggers(){document.querySelectorAll(this.opts.triggerSelector).forEach(t=>{const i=s=>{s.preventDefault(),this.handleTriggerClick(t)},r=this.boundClickHandlers.get(t);r&&t.removeEventListener("click",r),t.addEventListener("click",i),this.boundClickHandlers.set(t,i)})}unbindTriggers(){this.boundClickHandlers.forEach((e,t)=>{t.removeEventListener("click",e)}),this.boundClickHandlers.clear()}handleTriggerClick(e){const t=e.getAttribute("data-moment-team-slug")??"",i=e.getAttribute("data-moment-slug")??void 0,r=e.getAttribute("data-moment-list-slug")??void 0,s=e.getAttribute("data-moment-trigger-type");let n="moment";s!=null&&s!==""&&(s==="moment"||s==="list"||s==="team"?n=s:console.warn("[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",s));const d=e.getAttribute("data-moment-ids")??e.getAttribute("data-moment-game-id")??"",m=d?d.split(",").filter(Boolean):[],a=e.getAttribute("data-moment-calendar");let u;if(a!=null&&a!==""&&(a==="google"||a==="outlook"?u=a:console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:",a)),!t){console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");return}const l=n==="team"?void 0:i,c=n==="team"?void 0:r,p=n==="team"?[]:m;this.emitAnalytics("embed.trigger.click",{teamSlug:t,triggerType:n,momentIds:p}),this.open({teamSlug:t,momentSlug:l,listSlug:c,triggerType:n,ids:p,calendar:u??void 0})}createModal(e){this.cleanup(),this.previousFocusedElement=document.activeElement||null,this.activePayload=e;const t=this.buildIframeUrl(e);this.overlay=document.createElement("div"),this.overlay.setAttribute("role","presentation"),Object.assign(this.overlay.style,{position:"fixed",inset:"0",zIndex:"999999",display:"flex",alignItems:"center",justifyContent:"center",backgroundColor:"rgba(0, 0, 0, 0.5)",backdropFilter:"blur(2px)",padding:"16px"}),this.overlay.addEventListener("click",l=>{l.target===this.overlay&&this.handleCancel()});const i=l=>{l.key==="Escape"&&this.handleCancel()};document.addEventListener("keydown",i);const r=document.createElement("div");Object.assign(r.style,{position:"relative",width:"100%",maxWidth:"380px",borderRadius:"16px",overflow:"hidden",backgroundColor:"#000000",boxShadow:"0 25px 50px -12px rgba(0, 0, 0, 0.25)",display:"flex",flexDirection:"column"});const s=document.createElement("div");s.setAttribute("role","dialog"),s.setAttribute("aria-modal","true"),s.setAttribute("aria-label","Calendar sync modal"),s.tabIndex=-1,Object.assign(s.style,{position:"relative",width:"100%",maxHeight:"80vh",borderRadius:"12px",overflow:"auto",backgroundColor:"#ffffff"});const n=document.createElement("button");n.type="button",n.innerHTML='<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>',n.setAttribute("aria-label","Close"),Object.assign(n.style,{position:"absolute",top:"10px",right:"10px",zIndex:"10",border:"none",backgroundColor:"transparent",color:"#999",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",width:"24px",height:"24px",padding:"0"}),n.addEventListener("mouseenter",()=>{n.style.color="#666"}),n.addEventListener("mouseleave",()=>{n.style.color="#999"}),n.addEventListener("click",()=>this.handleCancel()),this.iframe=document.createElement("iframe"),this.iframe.src=t,this.iframe.setAttribute("allow",""),this.iframe.setAttribute("sandbox","allow-scripts allow-same-origin allow-popups allow-forms"),this.iframe.setAttribute("scrolling","no"),Object.assign(this.iframe.style,{width:"100%",border:"none",display:"block",height:"420px",overflow:"hidden",transition:"height 0.15s ease"}),s.appendChild(n),s.appendChild(this.iframe);const d=document.createElement("div");Object.assign(d.style,{display:"flex",alignItems:"center",justifyContent:"center",gap:"6px",padding:"12px 0"});const m=document.createElement("span");m.textContent="Powered by",Object.assign(m.style,{fontFamily:"Inter, system-ui, -apple-system, sans-serif",fontSize:"12px",lineHeight:"16px",color:"#ffffff"});const a=document.createElement("img");a.alt="Moment",a.src=`${this.liveBaseUrl}/brand/moment-text-white.png`,Object.assign(a.style,{height:"auto",width:"67px"}),d.appendChild(m),d.appendChild(a),r.appendChild(s),r.appendChild(d),this.overlay.appendChild(r),document.body.appendChild(this.overlay);const u=l=>{if(l.key!=="Tab")return;const c=s.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');if(c.length===0){l.preventDefault(),s.focus();return}const p=c[0],b=c[c.length-1],y=document.activeElement;l.shiftKey&&y===p?(l.preventDefault(),b.focus()):!l.shiftKey&&y===b&&(l.preventDefault(),p.focus())};s.addEventListener("keydown",u),n.focus(),this.previousBodyOverflow=document.body.style.overflow,document.body.style.overflow="hidden",this.didManageBodyOverflow=!0,this.setupMessageListener(),this.overlay._escHandler=i,this.overlay._focusTrapHandler=u}buildIframeUrl(e){const t=new URLSearchParams;return t.set("teamSlug",e.teamSlug),e.momentSlug&&t.set("momentSlug",e.momentSlug),e.listSlug&&t.set("listSlug",e.listSlug),t.set("triggerType",e.triggerType),e.ids?.length&&t.set("ids",e.ids.join(",")),e.calendar&&t.set("calendar",e.calendar),t.set("returnOrigin",window.location.origin),t.set("embed","1"),`${this.liveBaseUrl}/en/embed/sync?${t.toString()}`}setupMessageListener(){this.messageHandler=e=>{if(e.origin!==this.liveOrigin||e.source!==this.iframe?.contentWindow)return;const t=e.data;if(!(!t||typeof t!="object")){if(t.source==="moment-live-embed"&&t.type==="moment.embed.oauth.start"){const i=t;this.emitAnalytics("oauth.start",{provider:i.provider,teamSlug:i.teamSlug}),this.handleOAuthStart(i);return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.resize"){const i=typeof t.height=="number"?t.height:0;if(i>0&&this.iframe){const r=Math.round(window.innerHeight*.8);this.iframe.style.height=`${Math.min(i,r)}px`}return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.open.url"){const{url:i}=t;i&&typeof i=="string"&&i.startsWith("webcal://")&&window.open(i,"_blank");return}if(t.source==="moment-live-embed"&&t.type==="moment.embed.result"){const i=t;this.handleResult(i)}}},window.addEventListener("message",this.messageHandler)}handleOAuthStart(e){this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.popupBridge=new w({liveBaseUrl:this.liveBaseUrl,liveOrigin:this.liveOrigin,iframeWindow:this.iframe?.contentWindow??null,onResult:t=>{this.handleResult(t)},onPopupBlocked:()=>{this.emitAnalytics("oauth.popup.blocked",{provider:e.provider})},onPopupClosed:()=>{this.emitAnalytics("oauth.popup.closed",{provider:e.provider})}}),this.popupBridge.open(e)}handleResult(e){if(e.result==="success")this.emitAnalytics("subscribe.success",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onSuccess?.(e);else if(e.result==="error")this.emitAnalytics("subscribe.error",{provider:e.provider,triggerType:e.triggerType,momentIds:e.momentIds}),this.opts.onError?.(e);else if(e.result==="cancelled")return;this.opts.onClose?.(e),this.emitAnalytics("embed.close"),this.closeTimer=setTimeout(()=>this.cleanup(),1500)}handleCancel(){this.emitAnalytics("embed.close");const t={source:"moment-live-embed",type:"moment.embed.result",result:"cancelled",triggerType:this.activePayload?.triggerType??"moment",momentIds:this.activePayload?.ids??[]};this.opts.onClose?.(t),this.cleanup()}cleanup(){if(this.closeTimer&&(clearTimeout(this.closeTimer),this.closeTimer=null),this.popupBridge&&(this.popupBridge.cleanup(),this.popupBridge=null),this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.overlay){const{_escHandler:e,_focusTrapHandler:t}=this.overlay;e&&document.removeEventListener("keydown",e);const i=this.overlay.firstElementChild;i&&t&&i.removeEventListener("keydown",t),this.overlay.remove(),this.overlay=null}this.iframe=null,this.activePayload=null,this.didManageBodyOverflow&&(document.body.style.overflow=this.previousBodyOverflow,this.didManageBodyOverflow=!1),this.previousFocusedElement&&typeof this.previousFocusedElement.focus=="function"&&this.previousFocusedElement.focus(),this.previousFocusedElement=null}emitAnalytics(e,t){this.opts.onAnalytics?.({event:e,timestamp:Date.now(),...t})}}function v(o){const e=document.createElement("button");return e.type="button",e.classList.add("moment-sync-trigger"),o.className&&o.className.split(" ").forEach(t=>{t&&e.classList.add(t)}),e.setAttribute("data-moment-team-slug",o.teamSlug),o.momentSlug&&e.setAttribute("data-moment-slug",o.momentSlug),o.listSlug&&e.setAttribute("data-moment-list-slug",o.listSlug),o.triggerType&&e.setAttribute("data-moment-trigger-type",o.triggerType),o.ids?.length&&e.setAttribute("data-moment-ids",o.ids.join(",")),o.calendar&&e.setAttribute("data-moment-calendar",o.calendar),e.textContent=o.label??"Add to Calendar",e}if(typeof window<"u"){const o=window;o.MomentSdk=f,o.createMomentButton=v}h.MomentSdk=f,h.createMomentButton=v,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})})(this.MomentSdk=this.MomentSdk||{});
@@ -1,4 +1,4 @@
1
- class f {
1
+ class v {
2
2
  popup = null;
3
3
  pollTimer = null;
4
4
  messageHandler = null;
@@ -63,8 +63,38 @@ class f {
63
63
  this.opts.iframeWindow.postMessage(t, this.opts.liveOrigin);
64
64
  }
65
65
  }
66
- const v = ".moment-sync-trigger", b = "https://live.dev.momentco.ai";
67
- class y {
66
+ const f = "moment-sdk-trigger-styles", b = `
67
+ :where(.moment-sync-trigger) {
68
+ background-color: #1c1917;
69
+ color: #ffffff;
70
+ border: 1px solid #292524;
71
+ border-radius: 4px;
72
+ padding: 8px 16px;
73
+ font-size: 14px;
74
+ font-weight: 500;
75
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
76
+ line-height: 1.4;
77
+ cursor: pointer;
78
+ appearance: none;
79
+ -webkit-appearance: none;
80
+ }
81
+
82
+ :where(.moment-sync-trigger:hover) {
83
+ background-color: #292524;
84
+ }
85
+
86
+ :where(.moment-sync-trigger:focus-visible) {
87
+ outline: 2px solid #1c1917;
88
+ outline-offset: 2px;
89
+ }
90
+ `;
91
+ function y() {
92
+ if (typeof document > "u" || document.getElementById(f)) return;
93
+ const o = document.createElement("style");
94
+ o.id = f, o.textContent = b, document.head.appendChild(o);
95
+ }
96
+ const w = ".moment-sync-trigger", T = "https://live.dev.momentco.ai";
97
+ class E {
68
98
  opts;
69
99
  liveBaseUrl;
70
100
  liveOrigin;
@@ -80,15 +110,15 @@ class y {
80
110
  closeTimer = null;
81
111
  constructor(e) {
82
112
  this.opts = {
83
- triggerSelector: v,
113
+ triggerSelector: w,
84
114
  ...e
85
- }, this.liveBaseUrl = b;
115
+ }, this.liveBaseUrl = T;
86
116
  try {
87
117
  this.liveOrigin = new URL(this.liveBaseUrl).origin;
88
118
  } catch {
89
119
  throw new Error(`[MomentSdk] Invalid VITE_LIVE_BASE_URL: "${this.liveBaseUrl}"`);
90
120
  }
91
- this.bindTriggers(), this.emitAnalytics("embed.init");
121
+ y(), this.bindTriggers(), this.emitAnalytics("embed.init");
92
122
  }
93
123
  /**
94
124
  * Programmatically open the sync modal.
@@ -138,13 +168,13 @@ class y {
138
168
  "[MomentSdk] Invalid data-moment-trigger-type, falling back to 'moment':",
139
169
  s
140
170
  ));
141
- const d = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", u = d ? d.split(",").filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
142
- let m;
143
- if (a != null && a !== "" && (a === "google" || a === "outlook" ? m = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a)), !t) {
171
+ const d = e.getAttribute("data-moment-ids") ?? e.getAttribute("data-moment-game-id") ?? "", m = d ? d.split(",").filter(Boolean) : [], a = e.getAttribute("data-moment-calendar");
172
+ let u;
173
+ if (a != null && a !== "" && (a === "google" || a === "outlook" ? u = a : console.warn("[MomentSdk] Invalid data-moment-calendar, ignoring value:", a)), !t) {
144
174
  console.warn("[MomentSdk] Missing data-moment-team-slug on trigger element");
145
175
  return;
146
176
  }
147
- const l = n === "team" ? void 0 : i, c = n === "team" ? void 0 : r, p = n === "team" ? [] : u;
177
+ const l = n === "team" ? void 0 : i, c = n === "team" ? void 0 : r, p = n === "team" ? [] : m;
148
178
  this.emitAnalytics("embed.trigger.click", {
149
179
  teamSlug: t,
150
180
  triggerType: n,
@@ -155,7 +185,7 @@ class y {
155
185
  listSlug: c,
156
186
  triggerType: n,
157
187
  ids: p,
158
- calendar: m ?? void 0
188
+ calendar: u ?? void 0
159
189
  });
160
190
  }
161
191
  // ── Private: modal creation ───────────────────────────────────────
@@ -236,8 +266,8 @@ class y {
236
266
  gap: "6px",
237
267
  padding: "12px 0"
238
268
  });
239
- const u = document.createElement("span");
240
- u.textContent = "Powered by", Object.assign(u.style, {
269
+ const m = document.createElement("span");
270
+ m.textContent = "Powered by", Object.assign(m.style, {
241
271
  fontFamily: "Inter, system-ui, -apple-system, sans-serif",
242
272
  fontSize: "12px",
243
273
  lineHeight: "16px",
@@ -247,8 +277,8 @@ class y {
247
277
  a.alt = "Moment", a.src = `${this.liveBaseUrl}/brand/moment-text-white.png`, Object.assign(a.style, {
248
278
  height: "auto",
249
279
  width: "67px"
250
- }), d.appendChild(u), d.appendChild(a), r.appendChild(s), r.appendChild(d), this.overlay.appendChild(r), document.body.appendChild(this.overlay);
251
- const m = (l) => {
280
+ }), d.appendChild(m), d.appendChild(a), r.appendChild(s), r.appendChild(d), this.overlay.appendChild(r), document.body.appendChild(this.overlay);
281
+ const u = (l) => {
252
282
  if (l.key !== "Tab") return;
253
283
  const c = s.querySelectorAll(
254
284
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -260,7 +290,7 @@ class y {
260
290
  const p = c[0], h = c[c.length - 1], g = document.activeElement;
261
291
  l.shiftKey && g === p ? (l.preventDefault(), h.focus()) : !l.shiftKey && g === h && (l.preventDefault(), p.focus());
262
292
  };
263
- s.addEventListener("keydown", m), n.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = m;
293
+ s.addEventListener("keydown", u), n.focus(), this.previousBodyOverflow = document.body.style.overflow, document.body.style.overflow = "hidden", this.didManageBodyOverflow = !0, this.setupMessageListener(), this.overlay._escHandler = i, this.overlay._focusTrapHandler = u;
264
294
  }
265
295
  buildIframeUrl(e) {
266
296
  const t = new URLSearchParams();
@@ -301,7 +331,7 @@ class y {
301
331
  }, window.addEventListener("message", this.messageHandler);
302
332
  }
303
333
  handleOAuthStart(e) {
304
- this.popupBridge && (this.popupBridge.cleanup(), this.popupBridge = null), this.popupBridge = new f({
334
+ this.popupBridge && (this.popupBridge.cleanup(), this.popupBridge = null), this.popupBridge = new v({
305
335
  liveBaseUrl: this.liveBaseUrl,
306
336
  liveOrigin: this.liveOrigin,
307
337
  iframeWindow: this.iframe?.contentWindow ?? null,
@@ -367,27 +397,17 @@ class y {
367
397
  });
368
398
  }
369
399
  }
370
- function w(o) {
400
+ function S(o) {
371
401
  const e = document.createElement("button");
372
402
  return e.type = "button", e.classList.add("moment-sync-trigger"), o.className && o.className.split(" ").forEach((t) => {
373
403
  t && e.classList.add(t);
374
- }), Object.assign(e.style, {
375
- backgroundColor: "#1c1917",
376
- color: "#ffffff",
377
- border: "1px solid #292524",
378
- borderRadius: "4px",
379
- padding: "8px 16px",
380
- fontSize: "14px",
381
- fontWeight: "500",
382
- cursor: "pointer",
383
- lineHeight: "1.4"
384
404
  }), e.setAttribute("data-moment-team-slug", o.teamSlug), o.momentSlug && e.setAttribute("data-moment-slug", o.momentSlug), o.listSlug && e.setAttribute("data-moment-list-slug", o.listSlug), o.triggerType && e.setAttribute("data-moment-trigger-type", o.triggerType), o.ids?.length && e.setAttribute("data-moment-ids", o.ids.join(",")), o.calendar && e.setAttribute("data-moment-calendar", o.calendar), e.textContent = o.label ?? "Add to Calendar", e;
385
405
  }
386
406
  if (typeof window < "u") {
387
407
  const o = window;
388
- o.MomentSdk = y, o.createMomentButton = w;
408
+ o.MomentSdk = E, o.createMomentButton = S;
389
409
  }
390
410
  export {
391
- y as MomentSdk,
392
- w as createMomentButton
411
+ E as MomentSdk,
412
+ S as createMomentButton
393
413
  };
@@ -0,0 +1,3 @@
1
+ export declare const TRIGGER_STYLE_ELEMENT_ID = "moment-sdk-trigger-styles";
2
+ /** Inject default trigger button styles once per page. */
3
+ export declare function injectTriggerStyles(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momentco-ai/moment-sdk",
3
- "version": "0.2.0-dev.14",
3
+ "version": "0.2.1-dev.16",
4
4
  "type": "module",
5
5
  "description": "Embeddable calendar sync widget for external team websites",
6
6
  "author": "Moment Co. <support@momentco.ai> (https://momentco.ai)",