@withone/connect 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -0
- package/dist/button.d.ts +2 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/types/index.d.ts +38 -0
- package/package.json +1 -1
- package/src/button.ts +294 -0
- package/src/index.ts +4 -0
- package/src/types/index.d.ts +38 -0
- package/src/window/index.ts +1 -1
package/README.md
CHANGED
|
@@ -145,6 +145,57 @@ export function ConnectWithOne() {
|
|
|
145
145
|
| `open()` | Opens the Connect modal over the current page |
|
|
146
146
|
| `close()` | Tears down the modal frame and its listeners |
|
|
147
147
|
|
|
148
|
+
|
|
149
|
+
### Optional: the pre-built button
|
|
150
|
+
|
|
151
|
+
Any element wired to `open()` works — the button below is **optional**.
|
|
152
|
+
It ships in the same zero-dependency package, renders real DOM (so it
|
|
153
|
+
mounts identically from React, Vue, or vanilla JS), and manages its own
|
|
154
|
+
Connect → Connecting → Connected states by wrapping your callbacks.
|
|
155
|
+
Provider icons are yours to supply — the SDK contains no One URLs.
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
"use client";
|
|
159
|
+
import { useEffect, useRef } from "react";
|
|
160
|
+
import { mountConnectButton } from "@withone/connect";
|
|
161
|
+
|
|
162
|
+
export function ConnectWithOne() {
|
|
163
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
164
|
+
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
const button = mountConnectButton(ref.current!, {
|
|
167
|
+
connect: {
|
|
168
|
+
authorize: { url: `${window.location.origin}/api/one/authorize` },
|
|
169
|
+
appTheme: "light",
|
|
170
|
+
onSuccess: () => {/* refresh your app state */},
|
|
171
|
+
},
|
|
172
|
+
label: "Connect your apps",
|
|
173
|
+
variant: "block", // "default" | "accent" | "block"
|
|
174
|
+
theme: "light", // matches YOUR page
|
|
175
|
+
platforms: [
|
|
176
|
+
{ name: "Stripe", imageUrl: "/icons/stripe.svg" },
|
|
177
|
+
{ name: "PostHog", imageUrl: "/icons/posthog.svg" },
|
|
178
|
+
],
|
|
179
|
+
description:
|
|
180
|
+
"Bring your tools into this workspace. You pick what gets shared.",
|
|
181
|
+
connectedLabel: "Apps connected",
|
|
182
|
+
});
|
|
183
|
+
return () => button.destroy();
|
|
184
|
+
}, []);
|
|
185
|
+
|
|
186
|
+
return <div ref={ref} />;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
| Variant | Look |
|
|
191
|
+
|---|---|
|
|
192
|
+
| `default` | Neutral pill: provider chips (fan on hover), label, arrow |
|
|
193
|
+
| `accent` | Same pill in your brand color (`accentColor`; lime fallback) |
|
|
194
|
+
| `block` | Full-width consent card: title + chips, description, "Secured by One" foot |
|
|
195
|
+
|
|
196
|
+
The returned handle has `setState("idle" | "connecting" | "connected")`
|
|
197
|
+
for manual control and `destroy()` for teardown.
|
|
198
|
+
|
|
148
199
|
## 3 · Backend — the authorize route
|
|
149
200
|
|
|
150
201
|
Generates `state` (CSRF proof) and PKCE (proof that whoever redeems the code is this server), stashes both in an httpOnly cookie, and 302s the browser to One.
|
package/dist/button.d.ts
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { useOneConnect } from "./useOneConnect";
|
|
2
2
|
export { completeOneConnect } from "./complete";
|
|
3
|
-
export
|
|
3
|
+
export { mountConnectButton } from "./button";
|
|
4
|
+
export type { OneConnectProps, OneConnectHandle, OneConnectResult, ConnectButtonOptions, ConnectButtonPlatform, ConnectButtonHandle, } from "./types";
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e="@withone/connect:result",t="one-connect-frame";function n(){const e=document.getElementById(t);e&&e.remove()}const o="one-connect-success";function i(){var e;null===(e=document.getElementById(o))||void 0===e||e.remove()}function r(){return document.getElementById(t)}const s=s=>{let a=null,c=!1;const d=()=>{"undefined"!=typeof window&&a&&(window.removeEventListener("message",a),a=null),n()},l=(e,t)=>{if(!c){c=!0,d(),"success"===e&&function(e){var t;i();const n="dark"===e,r=document.createElement("div");r.id=o,Object.assign(r.style,{position:"fixed",inset:"0",zIndex:"2147483000",display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(8, 8, 8, 0.5)",opacity:"0",transition:"opacity 160ms ease"});const s=document.createElement("div");Object.assign(s.style,{width:"520px",maxWidth:"calc(100vw - 16px)",height:"520px",maxHeight:"calc(100vh - 16px)",display:"flex",flexDirection:"column",textAlign:"center",fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"});const a=n?"#a1a1aa":"#6b7280",c=n?"rgba(25, 25, 25, 0.97)":"rgba(255, 255, 255, 0.97)",d=n?"rgba(255,255,255,0.08)":"rgba(228,228,223,0.9)",l=n?"rgba(45, 45, 44, 0.9)":"rgba(232, 232, 228, 0.95)";s.innerHTML=`<div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:40px 48px;border-radius:28px 28px 0 0;background:${c};border:1px solid ${d};border-bottom:0;"><button data-one-close aria-label="Close" style="position:absolute;top:16px;right:16px;width:20px;height:20px;padding:0;border:0;background:none;cursor:pointer;color:${a};line-height:0;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg></button><div style="width:56px;height:56px;border-radius:50%;background:#10b981;display:flex;align-items:center;justify-content:center;"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg></div><div style="font-size:18px;font-weight:600;letter-spacing:-0.01em;color:${n?"#fafafa":"#111114"};">Access granted</div><div style="font-size:13px;line-height:1.5;max-width:280px;color:${a};">Your tools are connected. You can pick up right where you left off.</div><button data-one-close style="width:100%;max-width:320px;margin-top:8px;padding:11px 0;border:0;border-radius:12px;cursor:pointer;font-size:14px;font-weight:500;background:${n?"#fafafa":"#111114"};color:${n?"#111114":"#fafafa"};">Close</button></div><div style="height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;border-radius:0 0 28px 28px;background:${l};border:1px solid ${d};border-top:0;"><span style="font-size:11px;color:${a};">Secured by</span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${n?"#fafafa":"#111114"};">one</span></div>`,r.appendChild(s),document.body.appendChild(r),requestAnimationFrame(()=>{r.style.opacity="1"});const u=()=>{window.removeEventListener("keydown",p),r.style.opacity="0",window.setTimeout(()=>r.remove(),180)};function p(e){"Escape"===e.key&&u()}for(const e of s.querySelectorAll("[data-one-close]"))e.addEventListener("click",u);r.addEventListener("click",e=>{e.target===r&&u()}),window.addEventListener("keydown",p),null===(t=s.querySelector("[data-one-close]"))||void 0===t||t.focus()}(s.appTheme);try{var n,r;if("success"===e)null===(n=s.onSuccess)||void 0===n||n.call(s);else null===(r=s.onError)||void 0===r||r.call(s,null!=t?t:"The connection was not completed.")}catch{}}},u=t=>{const n=t.data;if(!n)return;const o=r();if(o&&t.source===o.contentWindow)if("@withone/connect:exit"!==n.type)n.type!==e||t.origin!==window.location.origin||"success"!==n.status&&"error"!==n.status||l(n.status,n.message);else{d();try{var i;null===(i=s.onClose)||void 0===i||i.call(s)}catch{}}},p=()=>{var e;const t=r();if(!t)return;let n,o;try{var i,s;n=null!==(i=null===(s=t.contentWindow)||void 0===s?void 0:s.location.href)&&void 0!==i?i:""}catch{return}try{o=new URL(n).searchParams}catch{return}const a=o.get("one_connect");"success"!==a&&"error"!==a||(t.style.visibility="hidden",l(a,null!==(e=o.get("one_connect_message"))&&void 0!==e?e:void 0))};return{open:()=>{if("undefined"==typeof window)return;c=!1,a=u,window.addEventListener("message",a);(function(e){n();const o=document.createElement("iframe");return o.id=t,o.src=e,o.setAttribute("allowtransparency","true"),Object.assign(o.style,{position:"fixed",inset:"0",width:"100%",height:"100%",border:"0",zIndex:"2147483000",background:"transparent",colorScheme:"normal"}),document.body.appendChild(o),o})((()=>{try{const e=new URL(s.authorize.url);return s.appTheme&&(e.hash=`one_theme=${s.appTheme}`),e.toString()}catch{return s.authorize.url}})()).addEventListener("load",p)},close:()=>{i(),d()}}};function a(t={status:"success"}){if("undefined"==typeof window)return!1;if(window.parent&&window.parent!==window){const n={type:e,status:t.status,message:t.message};try{return window.parent.postMessage(n,window.location.origin),!0}catch{return!1}}return!1}export{a as completeOneConnect,s as useOneConnect};
|
|
1
|
+
const e="@withone/connect:result",t="one-connect-frame";function n(){const e=document.getElementById(t);e&&e.remove()}const o="one-connect-success";function i(){var e;null===(e=document.getElementById(o))||void 0===e||e.remove()}function c(){return document.getElementById(t)}const r=r=>{let s=null,a=!1;const l=()=>{"undefined"!=typeof window&&s&&(window.removeEventListener("message",s),s=null),n()},d=(e,t)=>{if(!a){a=!0,l(),"success"===e&&function(e){var t;i();const n="dark"===e,c=document.createElement("div");c.id=o,Object.assign(c.style,{position:"fixed",inset:"0",zIndex:"2147483000",display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(8, 8, 8, 0.5)",opacity:"0",transition:"opacity 160ms ease"});const r=document.createElement("div");Object.assign(r.style,{width:"520px",maxWidth:"calc(100vw - 16px)",height:"520px",maxHeight:"calc(100vh - 16px)",display:"flex",flexDirection:"column",textAlign:"center",fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"});const s=n?"#a1a1aa":"#6b7280",a=n?"rgba(25, 25, 25, 0.97)":"rgba(255, 255, 255, 0.97)",l=n?"rgba(255,255,255,0.08)":"rgba(228,228,223,0.9)",d=n?"rgba(45, 45, 44, 0.9)":"rgba(232, 232, 228, 0.95)";r.innerHTML=`<div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:40px 48px;border-radius:28px 28px 0 0;background:${a};border:1px solid ${l};border-bottom:0;"><button data-one-close aria-label="Close" style="position:absolute;top:16px;right:16px;width:20px;height:20px;padding:0;border:0;background:none;cursor:pointer;color:${s};line-height:0;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg></button><div style="width:56px;height:56px;border-radius:50%;background:#10b981;display:flex;align-items:center;justify-content:center;"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg></div><div style="font-size:18px;font-weight:600;letter-spacing:-0.01em;color:${n?"#fafafa":"#111114"};">Access granted</div><div style="font-size:13px;line-height:1.5;max-width:280px;color:${s};">Your tools are connected. You can pick up right where you left off.</div><button data-one-close style="width:100%;margin-top:8px;padding:11px 0;border:0;border-radius:12px;cursor:pointer;font-size:14px;font-weight:500;background:${n?"#fafafa":"#111114"};color:${n?"#111114":"#fafafa"};">Close</button></div><div style="height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;border-radius:0 0 28px 28px;background:${d};border:1px solid ${l};border-top:0;"><span style="font-size:11px;color:${s};">Secured by</span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${n?"#fafafa":"#111114"};">one</span></div>`,c.appendChild(r),document.body.appendChild(c),requestAnimationFrame(()=>{c.style.opacity="1"});const p=()=>{window.removeEventListener("keydown",u),c.style.opacity="0",window.setTimeout(()=>c.remove(),180)};function u(e){"Escape"===e.key&&p()}for(const e of r.querySelectorAll("[data-one-close]"))e.addEventListener("click",p);c.addEventListener("click",e=>{e.target===c&&p()}),window.addEventListener("keydown",u),null===(t=r.querySelector("[data-one-close]"))||void 0===t||t.focus()}(r.appTheme);try{var n,c;if("success"===e)null===(n=r.onSuccess)||void 0===n||n.call(r);else null===(c=r.onError)||void 0===c||c.call(r,null!=t?t:"The connection was not completed.")}catch{}}},p=t=>{const n=t.data;if(!n)return;const o=c();if(o&&t.source===o.contentWindow)if("@withone/connect:exit"!==n.type)n.type!==e||t.origin!==window.location.origin||"success"!==n.status&&"error"!==n.status||d(n.status,n.message);else{l();try{var i;null===(i=r.onClose)||void 0===i||i.call(r)}catch{}}},u=()=>{var e;const t=c();if(!t)return;let n,o;try{var i,r;n=null!==(i=null===(r=t.contentWindow)||void 0===r?void 0:r.location.href)&&void 0!==i?i:""}catch{return}try{o=new URL(n).searchParams}catch{return}const s=o.get("one_connect");"success"!==s&&"error"!==s||(t.style.visibility="hidden",d(s,null!==(e=o.get("one_connect_message"))&&void 0!==e?e:void 0))};return{open:()=>{if("undefined"==typeof window)return;a=!1,s=p,window.addEventListener("message",s);(function(e){n();const o=document.createElement("iframe");return o.id=t,o.src=e,o.setAttribute("allowtransparency","true"),Object.assign(o.style,{position:"fixed",inset:"0",width:"100%",height:"100%",border:"0",zIndex:"2147483000",background:"transparent",colorScheme:"normal"}),document.body.appendChild(o),o})((()=>{try{const e=new URL(r.authorize.url);return r.appTheme&&(e.hash=`one_theme=${r.appTheme}`),e.toString()}catch{return r.authorize.url}})()).addEventListener("load",u)},close:()=>{i(),l()}}};function s(t={status:"success"}){if("undefined"==typeof window)return!1;if(window.parent&&window.parent!==window){const n={type:e,status:t.status,message:t.message};try{return window.parent.postMessage(n,window.location.origin),!0}catch{return!1}}return!1}const a="one-connect-button-styles",l="cubic-bezier(.2,.9,.25,1)",d={btnBg:"#0A0C0B",btnFg:"#ffffff",surface:"#ffffff",fg:"#0A0C0B",muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},p={btnBg:"#F2F5F3",btnFg:"#0A0C0B",surface:"#101312",fg:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"},u="#CCFF00",f="#3FE3A5",m="#0A0C0B";const h=(e,t)=>{var n,o;!function(){if("undefined"==typeof document||document.getElementById(a))return;const e=document.createElement("style");e.id=a,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${l},box-shadow .2s ${l},opacity .2s ${l}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${f};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${l};position:relative}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",".owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em}",".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${l},opacity .2s ${l}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const i="dark"===t.theme?p:d,c=null!==(n=t.variant)&&void 0!==n?n:"default",s=null!==(o=t.label)&&void 0!==o?o:"Connect your apps";let h="idle",b=!1;const g=r({...t.connect,onSuccess:()=>{var e,n;y("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;y("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)},onClose:()=>{var e,n;y("idle"),null===(e=(n=t.connect).onClose)||void 0===e||e.call(n)}}),w=document.createElement("button");w.type="button",w.className="block"===c?"owcb owcb-block":"owcb",w.addEventListener("click",()=>{"idle"!==h&&"connected"!==h||(y("connecting"),g.open())});const x=()=>{var e,n,o;const r="accent"===c,a="block"===c,l=a?i.surface:r?null!==(e=t.accentColor)&&void 0!==e?e:u:i.btnBg,d=a?i.fg:r?m:i.btnFg,p=a?i.surface:l,b=a?i.muted:d;w.style.background="connected"!==h||a?l:f,w.style.color="connected"!==h||a?d:m,w.style.boxShadow=a?`inset 0 0 0 1px ${i.line}`:"connected"===h?"none":r?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:u}38`:i.shadow,w.disabled="connecting"===h,w.innerHTML="";const g=function(e,t,n){var o,i;const c=null!==(o=e.platforms)&&void 0!==o?o:[];if(0===c.length&&!e.moreCount)return null;const r=document.createElement("span");r.className="owcb-stack",r.setAttribute("aria-hidden","true");for(const e of c.slice(0,4)){const n=document.createElement("span");if(n.className="owcb-chip",n.style.background="#ffffff",n.style.boxShadow=`0 0 0 1.5px ${t}`,e.imageUrl){const t=document.createElement("img");t.alt="",t.src=e.imageUrl,t.addEventListener("error",()=>{t.style.display="none",n.textContent=e.name.charAt(0).toUpperCase(),n.style.font="600 10px ui-monospace,monospace",n.style.color=m}),n.appendChild(t)}else n.textContent=e.name.charAt(0).toUpperCase(),n.style.font="600 10px ui-monospace,monospace",n.style.color=m;r.appendChild(n)}const s=(null!==(i=e.moreCount)&&void 0!==i?i:0)+Math.max(0,c.length-4);if(s>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.background="#ffffff",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.style.color=n,e.textContent=`+${s}`,r.appendChild(e)}return r}(t,p,b);if(a){var x;const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent="connecting"===h?"Connecting…":"connected"===h?null!==(x=t.connectedLabel)&&void 0!==x?x:"Connected":s,e.appendChild(n),"connecting"===h){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else g&&e.appendChild(g);if(w.appendChild(e),t.description&&"idle"===h){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=i.muted,e.textContent=t.description,w.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=i.muted,o.style.borderTop=`1px solid ${i.line}`,o.innerHTML=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;flex-shrink:0"><path d="M8 1.5l5 2v4c0 3-2.2 5.6-5 7-2.8-1.4-5-4-5-7v-4l5-2z"/></svg><span>Secured by <b style="color:${i.fg};font-weight:600">One</b></span>`,void w.appendChild(o)}if("connecting"===h){const e=document.createElement("span");e.className="owcb-spinner",w.appendChild(e)}else if("connected"===h){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",w.appendChild(e)}else g&&w.appendChild(g);const y=document.createElement("span");if(y.className="owcb-label",y.textContent="connecting"===h?"Connecting…":"connected"===h?null!==(o=t.connectedLabel)&&void 0!==o?o:"Connected":s,w.appendChild(y),"idle"===h){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",w.appendChild(e)}},y=e=>{b||(h=e,x())};return x(),e.appendChild(w),{setState:y,destroy:()=>{b=!0,g.close(),w.remove()}}};export{s as completeOneConnect,h as mountConnectButton,r as useOneConnect};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const e="@withone/connect:result",t="one-connect-frame";function n(){const e=document.getElementById(t);e&&e.remove()}const o="one-connect-success";function i(){var e;null===(e=document.getElementById(o))||void 0===e||e.remove()}function r(){return document.getElementById(t)}exports.completeOneConnect=function(t={status:"success"}){if("undefined"==typeof window)return!1;if(window.parent&&window.parent!==window){const n={type:e,status:t.status,message:t.message};try{return window.parent.postMessage(n,window.location.origin),!0}catch{return!1}}return!1},exports.useOneConnect=s=>{let a=null,c=!1;const d=()=>{"undefined"!=typeof window&&a&&(window.removeEventListener("message",a),a=null),n()},l=(e,t)=>{if(!c){c=!0,d(),"success"===e&&function(e){var t;i();const n="dark"===e,r=document.createElement("div");r.id=o,Object.assign(r.style,{position:"fixed",inset:"0",zIndex:"2147483000",display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(8, 8, 8, 0.5)",opacity:"0",transition:"opacity 160ms ease"});const s=document.createElement("div");Object.assign(s.style,{width:"520px",maxWidth:"calc(100vw - 16px)",height:"520px",maxHeight:"calc(100vh - 16px)",display:"flex",flexDirection:"column",textAlign:"center",fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"});const a=n?"#a1a1aa":"#6b7280",c=n?"rgba(25, 25, 25, 0.97)":"rgba(255, 255, 255, 0.97)",d=n?"rgba(255,255,255,0.08)":"rgba(228,228,223,0.9)",l=n?"rgba(45, 45, 44, 0.9)":"rgba(232, 232, 228, 0.95)";s.innerHTML=`<div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:40px 48px;border-radius:28px 28px 0 0;background:${c};border:1px solid ${d};border-bottom:0;"><button data-one-close aria-label="Close" style="position:absolute;top:16px;right:16px;width:20px;height:20px;padding:0;border:0;background:none;cursor:pointer;color:${a};line-height:0;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg></button><div style="width:56px;height:56px;border-radius:50%;background:#10b981;display:flex;align-items:center;justify-content:center;"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg></div><div style="font-size:18px;font-weight:600;letter-spacing:-0.01em;color:${n?"#fafafa":"#111114"};">Access granted</div><div style="font-size:13px;line-height:1.5;max-width:280px;color:${a};">Your tools are connected. You can pick up right where you left off.</div><button data-one-close style="width:100%;max-width:320px;margin-top:8px;padding:11px 0;border:0;border-radius:12px;cursor:pointer;font-size:14px;font-weight:500;background:${n?"#fafafa":"#111114"};color:${n?"#111114":"#fafafa"};">Close</button></div><div style="height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;border-radius:0 0 28px 28px;background:${l};border:1px solid ${d};border-top:0;"><span style="font-size:11px;color:${a};">Secured by</span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${n?"#fafafa":"#111114"};">one</span></div>`,r.appendChild(s),document.body.appendChild(r),requestAnimationFrame(()=>{r.style.opacity="1"});const u=()=>{window.removeEventListener("keydown",p),r.style.opacity="0",window.setTimeout(()=>r.remove(),180)};function p(e){"Escape"===e.key&&u()}for(const e of s.querySelectorAll("[data-one-close]"))e.addEventListener("click",u);r.addEventListener("click",e=>{e.target===r&&u()}),window.addEventListener("keydown",p),null===(t=s.querySelector("[data-one-close]"))||void 0===t||t.focus()}(s.appTheme);try{var n,r;if("success"===e)null===(n=s.onSuccess)||void 0===n||n.call(s);else null===(r=s.onError)||void 0===r||r.call(s,null!=t?t:"The connection was not completed.")}catch{}}},u=t=>{const n=t.data;if(!n)return;const o=r();if(o&&t.source===o.contentWindow)if("@withone/connect:exit"!==n.type)n.type!==e||t.origin!==window.location.origin||"success"!==n.status&&"error"!==n.status||l(n.status,n.message);else{d();try{var i;null===(i=s.onClose)||void 0===i||i.call(s)}catch{}}},p=()=>{var e;const t=r();if(!t)return;let n,o;try{var i,s;n=null!==(i=null===(s=t.contentWindow)||void 0===s?void 0:s.location.href)&&void 0!==i?i:""}catch{return}try{o=new URL(n).searchParams}catch{return}const a=o.get("one_connect");"success"!==a&&"error"!==a||(t.style.visibility="hidden",l(a,null!==(e=o.get("one_connect_message"))&&void 0!==e?e:void 0))};return{open:()=>{if("undefined"==typeof window)return;c=!1,a=u,window.addEventListener("message",a);(function(e){n();const o=document.createElement("iframe");return o.id=t,o.src=e,o.setAttribute("allowtransparency","true"),Object.assign(o.style,{position:"fixed",inset:"0",width:"100%",height:"100%",border:"0",zIndex:"2147483000",background:"transparent",colorScheme:"normal"}),document.body.appendChild(o),o})((()=>{try{const e=new URL(s.authorize.url);return s.appTheme&&(e.hash=`one_theme=${s.appTheme}`),e.toString()}catch{return s.authorize.url}})()).addEventListener("load",p)},close:()=>{i(),d()}}};
|
|
1
|
+
"use strict";const e="@withone/connect:result",t="one-connect-frame";function n(){const e=document.getElementById(t);e&&e.remove()}const o="one-connect-success";function i(){var e;null===(e=document.getElementById(o))||void 0===e||e.remove()}function c(){return document.getElementById(t)}const r=r=>{let s=null,a=!1;const l=()=>{"undefined"!=typeof window&&s&&(window.removeEventListener("message",s),s=null),n()},d=(e,t)=>{if(!a){a=!0,l(),"success"===e&&function(e){var t;i();const n="dark"===e,c=document.createElement("div");c.id=o,Object.assign(c.style,{position:"fixed",inset:"0",zIndex:"2147483000",display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(8, 8, 8, 0.5)",opacity:"0",transition:"opacity 160ms ease"});const r=document.createElement("div");Object.assign(r.style,{width:"520px",maxWidth:"calc(100vw - 16px)",height:"520px",maxHeight:"calc(100vh - 16px)",display:"flex",flexDirection:"column",textAlign:"center",fontFamily:"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"});const s=n?"#a1a1aa":"#6b7280",a=n?"rgba(25, 25, 25, 0.97)":"rgba(255, 255, 255, 0.97)",l=n?"rgba(255,255,255,0.08)":"rgba(228,228,223,0.9)",d=n?"rgba(45, 45, 44, 0.9)":"rgba(232, 232, 228, 0.95)";r.innerHTML=`<div style="position:relative;flex:1;min-height:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;padding:40px 48px;border-radius:28px 28px 0 0;background:${a};border:1px solid ${l};border-bottom:0;"><button data-one-close aria-label="Close" style="position:absolute;top:16px;right:16px;width:20px;height:20px;padding:0;border:0;background:none;cursor:pointer;color:${s};line-height:0;"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 6L6 18M6 6l12 12"/></svg></button><div style="width:56px;height:56px;border-radius:50%;background:#10b981;display:flex;align-items:center;justify-content:center;"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 13l4 4L19 7"/></svg></div><div style="font-size:18px;font-weight:600;letter-spacing:-0.01em;color:${n?"#fafafa":"#111114"};">Access granted</div><div style="font-size:13px;line-height:1.5;max-width:280px;color:${s};">Your tools are connected. You can pick up right where you left off.</div><button data-one-close style="width:100%;margin-top:8px;padding:11px 0;border:0;border-radius:12px;cursor:pointer;font-size:14px;font-weight:500;background:${n?"#fafafa":"#111114"};color:${n?"#111114":"#fafafa"};">Close</button></div><div style="height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;border-radius:0 0 28px 28px;background:${d};border:1px solid ${l};border-top:0;"><span style="font-size:11px;color:${s};">Secured by</span><span style="font-size:12px;font-weight:600;letter-spacing:-0.02em;color:${n?"#fafafa":"#111114"};">one</span></div>`,c.appendChild(r),document.body.appendChild(c),requestAnimationFrame(()=>{c.style.opacity="1"});const p=()=>{window.removeEventListener("keydown",u),c.style.opacity="0",window.setTimeout(()=>c.remove(),180)};function u(e){"Escape"===e.key&&p()}for(const e of r.querySelectorAll("[data-one-close]"))e.addEventListener("click",p);c.addEventListener("click",e=>{e.target===c&&p()}),window.addEventListener("keydown",u),null===(t=r.querySelector("[data-one-close]"))||void 0===t||t.focus()}(r.appTheme);try{var n,c;if("success"===e)null===(n=r.onSuccess)||void 0===n||n.call(r);else null===(c=r.onError)||void 0===c||c.call(r,null!=t?t:"The connection was not completed.")}catch{}}},p=t=>{const n=t.data;if(!n)return;const o=c();if(o&&t.source===o.contentWindow)if("@withone/connect:exit"!==n.type)n.type!==e||t.origin!==window.location.origin||"success"!==n.status&&"error"!==n.status||d(n.status,n.message);else{l();try{var i;null===(i=r.onClose)||void 0===i||i.call(r)}catch{}}},u=()=>{var e;const t=c();if(!t)return;let n,o;try{var i,r;n=null!==(i=null===(r=t.contentWindow)||void 0===r?void 0:r.location.href)&&void 0!==i?i:""}catch{return}try{o=new URL(n).searchParams}catch{return}const s=o.get("one_connect");"success"!==s&&"error"!==s||(t.style.visibility="hidden",d(s,null!==(e=o.get("one_connect_message"))&&void 0!==e?e:void 0))};return{open:()=>{if("undefined"==typeof window)return;a=!1,s=p,window.addEventListener("message",s);(function(e){n();const o=document.createElement("iframe");return o.id=t,o.src=e,o.setAttribute("allowtransparency","true"),Object.assign(o.style,{position:"fixed",inset:"0",width:"100%",height:"100%",border:"0",zIndex:"2147483000",background:"transparent",colorScheme:"normal"}),document.body.appendChild(o),o})((()=>{try{const e=new URL(r.authorize.url);return r.appTheme&&(e.hash=`one_theme=${r.appTheme}`),e.toString()}catch{return r.authorize.url}})()).addEventListener("load",u)},close:()=>{i(),l()}}};const s="one-connect-button-styles",a="cubic-bezier(.2,.9,.25,1)",l={btnBg:"#0A0C0B",btnFg:"#ffffff",surface:"#ffffff",fg:"#0A0C0B",muted:"#6B7280",line:"#D1D5DB",shadow:"0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)"},d={btnBg:"#F2F5F3",btnFg:"#0A0C0B",surface:"#101312",fg:"#F2F5F3",muted:"#8A938E",line:"#2A302D",shadow:"0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)"},p="#CCFF00",u="#3FE3A5",f="#0A0C0B";exports.completeOneConnect=function(t={status:"success"}){if("undefined"==typeof window)return!1;if(window.parent&&window.parent!==window){const n={type:e,status:t.status,message:t.message};try{return window.parent.postMessage(n,window.location.origin),!0}catch{return!1}}return!1},exports.mountConnectButton=(e,t)=>{var n,o;!function(){if("undefined"==typeof document||document.getElementById(s))return;const e=document.createElement("style");e.id=s,e.textContent=[`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${a},box-shadow .2s ${a},opacity .2s ${a}}`,".owcb:hover{transform:translateY(-1px)}",".owcb:active{transform:translateY(0)}",`.owcb:focus-visible{outline:2px solid ${u};outline-offset:3px}`,".owcb[disabled]{cursor:default;opacity:.75;transform:none}",".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${a};position:relative}`,".owcb-chip:first-child{margin-left:0}",".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",".owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em}",".owcb:hover .owcb-chip{margin-left:-2px}",".owcb:hover .owcb-chip:first-child{margin-left:0}",".owcb-label{white-space:nowrap}",`.owcb-arrow{opacity:.5;transition:transform .2s ${a},opacity .2s ${a}}`,".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",".owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}","@keyframes owcb-spin{to{transform:rotate(360deg)}}",".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}","@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}"].join("\n"),document.head.appendChild(e)}();const i="dark"===t.theme?d:l,c=null!==(n=t.variant)&&void 0!==n?n:"default",m=null!==(o=t.label)&&void 0!==o?o:"Connect your apps";let h="idle",b=!1;const g=r({...t.connect,onSuccess:()=>{var e,n;y("connected"),null===(e=(n=t.connect).onSuccess)||void 0===e||e.call(n)},onError:e=>{var n,o;y("idle"),null===(n=(o=t.connect).onError)||void 0===n||n.call(o,e)},onClose:()=>{var e,n;y("idle"),null===(e=(n=t.connect).onClose)||void 0===e||e.call(n)}}),w=document.createElement("button");w.type="button",w.className="block"===c?"owcb owcb-block":"owcb",w.addEventListener("click",()=>{"idle"!==h&&"connected"!==h||(y("connecting"),g.open())});const x=()=>{var e,n,o;const r="accent"===c,s="block"===c,a=s?i.surface:r?null!==(e=t.accentColor)&&void 0!==e?e:p:i.btnBg,l=s?i.fg:r?f:i.btnFg,d=s?i.surface:a,b=s?i.muted:l;w.style.background="connected"!==h||s?a:u,w.style.color="connected"!==h||s?l:f,w.style.boxShadow=s?`inset 0 0 0 1px ${i.line}`:"connected"===h?"none":r?`0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${null!==(n=t.accentColor)&&void 0!==n?n:p}38`:i.shadow,w.disabled="connecting"===h,w.innerHTML="";const g=function(e,t,n){var o,i;const c=null!==(o=e.platforms)&&void 0!==o?o:[];if(0===c.length&&!e.moreCount)return null;const r=document.createElement("span");r.className="owcb-stack",r.setAttribute("aria-hidden","true");for(const e of c.slice(0,4)){const n=document.createElement("span");if(n.className="owcb-chip",n.style.background="#ffffff",n.style.boxShadow=`0 0 0 1.5px ${t}`,e.imageUrl){const t=document.createElement("img");t.alt="",t.src=e.imageUrl,t.addEventListener("error",()=>{t.style.display="none",n.textContent=e.name.charAt(0).toUpperCase(),n.style.font="600 10px ui-monospace,monospace",n.style.color=f}),n.appendChild(t)}else n.textContent=e.name.charAt(0).toUpperCase(),n.style.font="600 10px ui-monospace,monospace",n.style.color=f;r.appendChild(n)}const s=(null!==(i=e.moreCount)&&void 0!==i?i:0)+Math.max(0,c.length-4);if(s>0){const e=document.createElement("span");e.className="owcb-chip owcb-more",e.style.background="#ffffff",e.style.boxShadow=`0 0 0 1.5px ${t}`,e.style.color=n,e.textContent=`+${s}`,r.appendChild(e)}return r}(t,d,b);if(s){var x;const e=document.createElement("span");e.className="owcb-block-top";const n=document.createElement("span");if(n.className="owcb-block-title",n.textContent="connecting"===h?"Connecting…":"connected"===h?null!==(x=t.connectedLabel)&&void 0!==x?x:"Connected":m,e.appendChild(n),"connecting"===h){const t=document.createElement("span");t.className="owcb-spinner",e.appendChild(t)}else g&&e.appendChild(g);if(w.appendChild(e),t.description&&"idle"===h){const e=document.createElement("span");e.className="owcb-block-sub",e.style.color=i.muted,e.textContent=t.description,w.appendChild(e)}const o=document.createElement("span");return o.className="owcb-block-foot",o.style.color=i.muted,o.style.borderTop=`1px solid ${i.line}`,o.innerHTML=`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;flex-shrink:0"><path d="M8 1.5l5 2v4c0 3-2.2 5.6-5 7-2.8-1.4-5-4-5-7v-4l5-2z"/></svg><span>Secured by <b style="color:${i.fg};font-weight:600">One</b></span>`,void w.appendChild(o)}if("connecting"===h){const e=document.createElement("span");e.className="owcb-spinner",w.appendChild(e)}else if("connected"===h){const e=document.createElement("span");e.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>',e.style.display="inline-flex",w.appendChild(e)}else g&&w.appendChild(g);const y=document.createElement("span");if(y.className="owcb-label",y.textContent="connecting"===h?"Connecting…":"connected"===h?null!==(o=t.connectedLabel)&&void 0!==o?o:"Connected":m,w.appendChild(y),"idle"===h){const e=document.createElement("span");e.innerHTML='<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>',e.style.display="inline-flex",w.appendChild(e)}},y=e=>{b||(h=e,x())};return x(),e.appendChild(w),{setState:y,destroy:()=>{b=!0,g.close(),w.remove()}}},exports.useOneConnect=r;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -50,3 +50,41 @@ export interface OneConnectMessage {
|
|
|
50
50
|
status: "success" | "error";
|
|
51
51
|
message?: string;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/** A provider chip on the pre-built button. The SDK ships no One URLs,
|
|
55
|
+
* so icon sources are the consumer's to provide. */
|
|
56
|
+
export interface ConnectButtonPlatform {
|
|
57
|
+
name: string;
|
|
58
|
+
imageUrl?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ConnectButtonOptions {
|
|
62
|
+
/** Everything useOneConnect takes — the button wires open() and the
|
|
63
|
+
* Connecting/Connected states around your callbacks. */
|
|
64
|
+
connect: OneConnectProps;
|
|
65
|
+
/** "Connect your apps" unless overridden. */
|
|
66
|
+
label?: string;
|
|
67
|
+
/** default = neutral pill; accent = brand-colored pill; block =
|
|
68
|
+
* full-width consent card with description + "Secured by One" foot. */
|
|
69
|
+
variant?: "default" | "accent" | "block";
|
|
70
|
+
/** Matches the host page, not One's card (that's connect.appTheme). */
|
|
71
|
+
theme?: "light" | "dark";
|
|
72
|
+
/** Provider chips fanned on hover; first four render, the rest fold
|
|
73
|
+
* into the +N chip together with moreCount. */
|
|
74
|
+
platforms?: ConnectButtonPlatform[];
|
|
75
|
+
/** Extra count for the +N chip (e.g. 274 for "the whole catalog"). */
|
|
76
|
+
moreCount?: number;
|
|
77
|
+
/** Sub-line on the block variant, shown while idle. */
|
|
78
|
+
description?: string;
|
|
79
|
+
/** accent variant fill; lime (#CCFF00) when omitted. */
|
|
80
|
+
accentColor?: string;
|
|
81
|
+
/** Label for the connected state ("4 apps connected"). */
|
|
82
|
+
connectedLabel?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ConnectButtonHandle {
|
|
86
|
+
/** Manually override the visual state. */
|
|
87
|
+
setState: (state: "idle" | "connecting" | "connected") => void;
|
|
88
|
+
/** Remove the button and tear down the flow wiring. */
|
|
89
|
+
destroy: () => void;
|
|
90
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@withone/connect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Frontend bindings for One Connect, a drop-in OAuth flow that lets your users grant your application scoped, revocable access to their own connected tools. The user owns the connections; you hold a token scoped to exactly what they granted.",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
package/src/button.ts
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { useOneConnect } from "./useOneConnect";
|
|
2
|
+
import type {
|
|
3
|
+
ConnectButtonHandle,
|
|
4
|
+
ConnectButtonOptions,
|
|
5
|
+
OneConnectHandle,
|
|
6
|
+
} from "./types";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Optional pre-built trigger for the connect flow. Entirely opt-in —
|
|
10
|
+
* `useOneConnect` alone with any element the consumer likes remains
|
|
11
|
+
* fully supported. Framework-agnostic like the rest of the SDK: it
|
|
12
|
+
* renders real DOM into a container, so React/Vue/vanilla all mount it
|
|
13
|
+
* the same way. Zero dependencies, zero One URLs — provider icons are
|
|
14
|
+
* passed in by the consumer.
|
|
15
|
+
*
|
|
16
|
+
* The button owns the flow wiring: click opens the card, the label
|
|
17
|
+
* turns "Connecting…" while the card is up, and lands on a spring
|
|
18
|
+
* "Connected" state on success (Connect → Connecting → Connected; the
|
|
19
|
+
* verb never changes).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const STYLE_ID = "one-connect-button-styles";
|
|
23
|
+
const EASE = "cubic-bezier(.2,.9,.25,1)";
|
|
24
|
+
|
|
25
|
+
interface Palette {
|
|
26
|
+
btnBg: string;
|
|
27
|
+
btnFg: string;
|
|
28
|
+
surface: string;
|
|
29
|
+
fg: string;
|
|
30
|
+
muted: string;
|
|
31
|
+
line: string;
|
|
32
|
+
shadow: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const LIGHT: Palette = {
|
|
36
|
+
btnBg: "#0A0C0B",
|
|
37
|
+
btnFg: "#ffffff",
|
|
38
|
+
surface: "#ffffff",
|
|
39
|
+
fg: "#0A0C0B",
|
|
40
|
+
muted: "#6B7280",
|
|
41
|
+
line: "#D1D5DB",
|
|
42
|
+
shadow: "0 1px 2px rgba(10,12,11,.08), 0 4px 14px rgba(10,12,11,.06)",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const DARK: Palette = {
|
|
46
|
+
btnBg: "#F2F5F3",
|
|
47
|
+
btnFg: "#0A0C0B",
|
|
48
|
+
surface: "#101312",
|
|
49
|
+
fg: "#F2F5F3",
|
|
50
|
+
muted: "#8A938E",
|
|
51
|
+
line: "#2A302D",
|
|
52
|
+
shadow: "0 1px 2px rgba(0,0,0,.4), 0 8px 24px rgba(0,0,0,.35)",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const LIME = "#CCFF00";
|
|
56
|
+
const SPRING = "#3FE3A5";
|
|
57
|
+
const CARBON = "#0A0C0B";
|
|
58
|
+
|
|
59
|
+
const SHIELD_SVG =
|
|
60
|
+
'<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round" style="width:12px;height:12px;flex-shrink:0"><path d="M8 1.5l5 2v4c0 3-2.2 5.6-5 7-2.8-1.4-5-4-5-7v-4l5-2z"/></svg>';
|
|
61
|
+
const ARROW_SVG =
|
|
62
|
+
'<svg class="owcb-arrow" width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3l5 5-5 5"/></svg>';
|
|
63
|
+
const TICK_SVG =
|
|
64
|
+
'<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px"><path d="M3 8.5l3.5 3.5L13 5"/></svg>';
|
|
65
|
+
|
|
66
|
+
function ensureStyles(): void {
|
|
67
|
+
if (typeof document === "undefined" || document.getElementById(STYLE_ID))
|
|
68
|
+
return;
|
|
69
|
+
const style = document.createElement("style");
|
|
70
|
+
style.id = STYLE_ID;
|
|
71
|
+
style.textContent = [
|
|
72
|
+
`.owcb{font:500 14.5px/1.2 inherit;font-family:inherit;letter-spacing:-.01em;cursor:pointer;border:0;display:inline-flex;align-items:center;gap:12px;padding:12px 16px 12px 14px;border-radius:12px;transition:transform .2s ${EASE},box-shadow .2s ${EASE},opacity .2s ${EASE}}`,
|
|
73
|
+
".owcb:hover{transform:translateY(-1px)}",
|
|
74
|
+
".owcb:active{transform:translateY(0)}",
|
|
75
|
+
`.owcb:focus-visible{outline:2px solid ${SPRING};outline-offset:3px}`,
|
|
76
|
+
".owcb[disabled]{cursor:default;opacity:.75;transform:none}",
|
|
77
|
+
".owcb-stack{display:inline-flex;align-items:center;flex-shrink:0}",
|
|
78
|
+
`.owcb-chip{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;overflow:hidden;margin-left:-7px;transition:margin-left .28s ${EASE};position:relative}`,
|
|
79
|
+
".owcb-chip:first-child{margin-left:0}",
|
|
80
|
+
".owcb-chip img{width:14px;height:14px;display:block;object-fit:contain}",
|
|
81
|
+
".owcb-chip.owcb-more{font-family:ui-monospace,monospace;font-size:9px;font-weight:500;letter-spacing:-.02em}",
|
|
82
|
+
".owcb:hover .owcb-chip{margin-left:-2px}",
|
|
83
|
+
".owcb:hover .owcb-chip:first-child{margin-left:0}",
|
|
84
|
+
".owcb-label{white-space:nowrap}",
|
|
85
|
+
`.owcb-arrow{opacity:.5;transition:transform .2s ${EASE},opacity .2s ${EASE}}`,
|
|
86
|
+
".owcb:hover .owcb-arrow{transform:translateX(2px);opacity:.8}",
|
|
87
|
+
`.owcb-spinner{width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;opacity:.7;animation:owcb-spin .7s linear infinite}`,
|
|
88
|
+
"@keyframes owcb-spin{to{transform:rotate(360deg)}}",
|
|
89
|
+
".owcb.owcb-block{display:flex;flex-direction:column;align-items:stretch;width:100%;max-width:420px;text-align:left;padding:16px;border-radius:14px;gap:0}",
|
|
90
|
+
".owcb-block-top{display:flex;align-items:center;justify-content:space-between;gap:12px}",
|
|
91
|
+
".owcb-block-title{font-size:15px;font-weight:600;letter-spacing:-.01em}",
|
|
92
|
+
".owcb-block-sub{font-size:13px;line-height:1.5;margin-top:6px;font-weight:400}",
|
|
93
|
+
".owcb-block-foot{margin-top:14px;padding-top:12px;display:flex;align-items:center;gap:6px;font-size:11.5px;font-weight:400}",
|
|
94
|
+
"@media (prefers-reduced-motion:reduce){.owcb,.owcb-chip,.owcb-arrow{transition:none}.owcb-spinner{animation-duration:1.4s}}",
|
|
95
|
+
].join("\n");
|
|
96
|
+
document.head.appendChild(style);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function buildStack(
|
|
100
|
+
options: ConnectButtonOptions,
|
|
101
|
+
chipRing: string,
|
|
102
|
+
moreColor: string,
|
|
103
|
+
): HTMLElement | null {
|
|
104
|
+
const platforms = options.platforms ?? [];
|
|
105
|
+
if (platforms.length === 0 && !options.moreCount) return null;
|
|
106
|
+
const stack = document.createElement("span");
|
|
107
|
+
stack.className = "owcb-stack";
|
|
108
|
+
stack.setAttribute("aria-hidden", "true");
|
|
109
|
+
for (const platform of platforms.slice(0, 4)) {
|
|
110
|
+
const chip = document.createElement("span");
|
|
111
|
+
chip.className = "owcb-chip";
|
|
112
|
+
chip.style.background = "#ffffff";
|
|
113
|
+
chip.style.boxShadow = `0 0 0 1.5px ${chipRing}`;
|
|
114
|
+
if (platform.imageUrl) {
|
|
115
|
+
const img = document.createElement("img");
|
|
116
|
+
img.alt = "";
|
|
117
|
+
img.src = platform.imageUrl;
|
|
118
|
+
img.addEventListener("error", () => {
|
|
119
|
+
img.style.display = "none";
|
|
120
|
+
chip.textContent = platform.name.charAt(0).toUpperCase();
|
|
121
|
+
chip.style.font = "600 10px ui-monospace,monospace";
|
|
122
|
+
chip.style.color = CARBON;
|
|
123
|
+
});
|
|
124
|
+
chip.appendChild(img);
|
|
125
|
+
} else {
|
|
126
|
+
chip.textContent = platform.name.charAt(0).toUpperCase();
|
|
127
|
+
chip.style.font = "600 10px ui-monospace,monospace";
|
|
128
|
+
chip.style.color = CARBON;
|
|
129
|
+
}
|
|
130
|
+
stack.appendChild(chip);
|
|
131
|
+
}
|
|
132
|
+
const extra =
|
|
133
|
+
(options.moreCount ?? 0) + Math.max(0, platforms.length - 4);
|
|
134
|
+
if (extra > 0) {
|
|
135
|
+
const more = document.createElement("span");
|
|
136
|
+
more.className = "owcb-chip owcb-more";
|
|
137
|
+
more.style.background = "#ffffff";
|
|
138
|
+
more.style.boxShadow = `0 0 0 1.5px ${chipRing}`;
|
|
139
|
+
more.style.color = moreColor;
|
|
140
|
+
more.textContent = `+${extra}`;
|
|
141
|
+
stack.appendChild(more);
|
|
142
|
+
}
|
|
143
|
+
return stack;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const mountConnectButton = (
|
|
147
|
+
container: HTMLElement,
|
|
148
|
+
options: ConnectButtonOptions,
|
|
149
|
+
): ConnectButtonHandle => {
|
|
150
|
+
ensureStyles();
|
|
151
|
+
|
|
152
|
+
const palette = options.theme === "dark" ? DARK : LIGHT;
|
|
153
|
+
const variant = options.variant ?? "default";
|
|
154
|
+
const label = options.label ?? "Connect your apps";
|
|
155
|
+
|
|
156
|
+
let state: "idle" | "connecting" | "connected" = "idle";
|
|
157
|
+
let destroyed = false;
|
|
158
|
+
|
|
159
|
+
const handle: OneConnectHandle = useOneConnect({
|
|
160
|
+
...options.connect,
|
|
161
|
+
onSuccess: () => {
|
|
162
|
+
setState("connected");
|
|
163
|
+
options.connect.onSuccess?.();
|
|
164
|
+
},
|
|
165
|
+
onError: (error) => {
|
|
166
|
+
setState("idle");
|
|
167
|
+
options.connect.onError?.(error);
|
|
168
|
+
},
|
|
169
|
+
onClose: () => {
|
|
170
|
+
setState("idle");
|
|
171
|
+
options.connect.onClose?.();
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const button = document.createElement("button");
|
|
176
|
+
button.type = "button";
|
|
177
|
+
button.className = variant === "block" ? "owcb owcb-block" : "owcb";
|
|
178
|
+
button.addEventListener("click", () => {
|
|
179
|
+
if (state !== "idle" && state !== "connected") return;
|
|
180
|
+
setState("connecting");
|
|
181
|
+
handle.open();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const paint = () => {
|
|
185
|
+
const accent = variant === "accent";
|
|
186
|
+
const block = variant === "block";
|
|
187
|
+
const bg = block
|
|
188
|
+
? palette.surface
|
|
189
|
+
: accent
|
|
190
|
+
? (options.accentColor ?? LIME)
|
|
191
|
+
: palette.btnBg;
|
|
192
|
+
const fg = block ? palette.fg : accent ? CARBON : palette.btnFg;
|
|
193
|
+
const chipRing = block ? palette.surface : bg;
|
|
194
|
+
const moreColor = block ? palette.muted : fg;
|
|
195
|
+
|
|
196
|
+
button.style.background = state === "connected" && !block ? SPRING : bg;
|
|
197
|
+
button.style.color = state === "connected" && !block ? CARBON : fg;
|
|
198
|
+
button.style.boxShadow = block
|
|
199
|
+
? `inset 0 0 0 1px ${palette.line}`
|
|
200
|
+
: state === "connected"
|
|
201
|
+
? "none"
|
|
202
|
+
: accent
|
|
203
|
+
? `0 1px 2px rgba(10,12,11,.1), 0 6px 20px ${(options.accentColor ?? LIME)}38`
|
|
204
|
+
: palette.shadow;
|
|
205
|
+
button.disabled = state === "connecting";
|
|
206
|
+
button.innerHTML = "";
|
|
207
|
+
|
|
208
|
+
const stack = buildStack(options, chipRing, moreColor);
|
|
209
|
+
|
|
210
|
+
if (block) {
|
|
211
|
+
const top = document.createElement("span");
|
|
212
|
+
top.className = "owcb-block-top";
|
|
213
|
+
const title = document.createElement("span");
|
|
214
|
+
title.className = "owcb-block-title";
|
|
215
|
+
title.textContent =
|
|
216
|
+
state === "connecting"
|
|
217
|
+
? "Connecting…"
|
|
218
|
+
: state === "connected"
|
|
219
|
+
? (options.connectedLabel ?? "Connected")
|
|
220
|
+
: label;
|
|
221
|
+
top.appendChild(title);
|
|
222
|
+
if (state === "connecting") {
|
|
223
|
+
const spinner = document.createElement("span");
|
|
224
|
+
spinner.className = "owcb-spinner";
|
|
225
|
+
top.appendChild(spinner);
|
|
226
|
+
} else if (stack) {
|
|
227
|
+
top.appendChild(stack);
|
|
228
|
+
}
|
|
229
|
+
button.appendChild(top);
|
|
230
|
+
if (options.description && state === "idle") {
|
|
231
|
+
const sub = document.createElement("span");
|
|
232
|
+
sub.className = "owcb-block-sub";
|
|
233
|
+
sub.style.color = palette.muted;
|
|
234
|
+
sub.textContent = options.description;
|
|
235
|
+
button.appendChild(sub);
|
|
236
|
+
}
|
|
237
|
+
const foot = document.createElement("span");
|
|
238
|
+
foot.className = "owcb-block-foot";
|
|
239
|
+
foot.style.color = palette.muted;
|
|
240
|
+
foot.style.borderTop = `1px solid ${palette.line}`;
|
|
241
|
+
foot.innerHTML = `${SHIELD_SVG}<span>Secured by <b style="color:${palette.fg};font-weight:600">One</b></span>`;
|
|
242
|
+
button.appendChild(foot);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (state === "connecting") {
|
|
247
|
+
const spinner = document.createElement("span");
|
|
248
|
+
spinner.className = "owcb-spinner";
|
|
249
|
+
button.appendChild(spinner);
|
|
250
|
+
} else if (state === "connected") {
|
|
251
|
+
const tick = document.createElement("span");
|
|
252
|
+
tick.innerHTML = TICK_SVG;
|
|
253
|
+
tick.style.display = "inline-flex";
|
|
254
|
+
button.appendChild(tick);
|
|
255
|
+
} else if (stack) {
|
|
256
|
+
button.appendChild(stack);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const text = document.createElement("span");
|
|
260
|
+
text.className = "owcb-label";
|
|
261
|
+
text.textContent =
|
|
262
|
+
state === "connecting"
|
|
263
|
+
? "Connecting…"
|
|
264
|
+
: state === "connected"
|
|
265
|
+
? (options.connectedLabel ?? "Connected")
|
|
266
|
+
: label;
|
|
267
|
+
button.appendChild(text);
|
|
268
|
+
|
|
269
|
+
if (state === "idle") {
|
|
270
|
+
const arrow = document.createElement("span");
|
|
271
|
+
arrow.innerHTML = ARROW_SVG;
|
|
272
|
+
arrow.style.display = "inline-flex";
|
|
273
|
+
button.appendChild(arrow);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const setState = (next: "idle" | "connecting" | "connected") => {
|
|
278
|
+
if (destroyed) return;
|
|
279
|
+
state = next;
|
|
280
|
+
paint();
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
paint();
|
|
284
|
+
container.appendChild(button);
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
setState,
|
|
288
|
+
destroy: () => {
|
|
289
|
+
destroyed = true;
|
|
290
|
+
handle.close();
|
|
291
|
+
button.remove();
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export { useOneConnect } from "./useOneConnect";
|
|
2
2
|
export { completeOneConnect } from "./complete";
|
|
3
|
+
export { mountConnectButton } from "./button";
|
|
3
4
|
export type {
|
|
4
5
|
OneConnectProps,
|
|
5
6
|
OneConnectHandle,
|
|
6
7
|
OneConnectResult,
|
|
8
|
+
ConnectButtonOptions,
|
|
9
|
+
ConnectButtonPlatform,
|
|
10
|
+
ConnectButtonHandle,
|
|
7
11
|
} from "./types";
|
package/src/types/index.d.ts
CHANGED
|
@@ -50,3 +50,41 @@ export interface OneConnectMessage {
|
|
|
50
50
|
status: "success" | "error";
|
|
51
51
|
message?: string;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
/** A provider chip on the pre-built button. The SDK ships no One URLs,
|
|
55
|
+
* so icon sources are the consumer's to provide. */
|
|
56
|
+
export interface ConnectButtonPlatform {
|
|
57
|
+
name: string;
|
|
58
|
+
imageUrl?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ConnectButtonOptions {
|
|
62
|
+
/** Everything useOneConnect takes — the button wires open() and the
|
|
63
|
+
* Connecting/Connected states around your callbacks. */
|
|
64
|
+
connect: OneConnectProps;
|
|
65
|
+
/** "Connect your apps" unless overridden. */
|
|
66
|
+
label?: string;
|
|
67
|
+
/** default = neutral pill; accent = brand-colored pill; block =
|
|
68
|
+
* full-width consent card with description + "Secured by One" foot. */
|
|
69
|
+
variant?: "default" | "accent" | "block";
|
|
70
|
+
/** Matches the host page, not One's card (that's connect.appTheme). */
|
|
71
|
+
theme?: "light" | "dark";
|
|
72
|
+
/** Provider chips fanned on hover; first four render, the rest fold
|
|
73
|
+
* into the +N chip together with moreCount. */
|
|
74
|
+
platforms?: ConnectButtonPlatform[];
|
|
75
|
+
/** Extra count for the +N chip (e.g. 274 for "the whole catalog"). */
|
|
76
|
+
moreCount?: number;
|
|
77
|
+
/** Sub-line on the block variant, shown while idle. */
|
|
78
|
+
description?: string;
|
|
79
|
+
/** accent variant fill; lime (#CCFF00) when omitted. */
|
|
80
|
+
accentColor?: string;
|
|
81
|
+
/** Label for the connected state ("4 apps connected"). */
|
|
82
|
+
connectedLabel?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ConnectButtonHandle {
|
|
86
|
+
/** Manually override the visual state. */
|
|
87
|
+
setState: (state: "idle" | "connecting" | "connected") => void;
|
|
88
|
+
/** Remove the button and tear down the flow wiring. */
|
|
89
|
+
destroy: () => void;
|
|
90
|
+
}
|
package/src/window/index.ts
CHANGED
|
@@ -94,7 +94,7 @@ export function showSuccessOverlay(theme?: "dark" | "light"): void {
|
|
|
94
94
|
"</div>" +
|
|
95
95
|
`<div style="font-size:18px;font-weight:600;letter-spacing:-0.01em;color:${dark ? "#fafafa" : "#111114"};">Access granted</div>` +
|
|
96
96
|
`<div style="font-size:13px;line-height:1.5;max-width:280px;color:${muted};">Your tools are connected. You can pick up right where you left off.</div>` +
|
|
97
|
-
`<button data-one-close style="width:100%;
|
|
97
|
+
`<button data-one-close style="width:100%;margin-top:8px;padding:11px 0;border:0;border-radius:12px;cursor:pointer;font-size:14px;font-weight:500;background:${dark ? "#fafafa" : "#111114"};color:${dark ? "#111114" : "#fafafa"};">Close</button>` +
|
|
98
98
|
"</div>" +
|
|
99
99
|
`<div style="height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;border-radius:0 0 28px 28px;background:${footerBg};border:1px solid ${cardBorder};border-top:0;">` +
|
|
100
100
|
`<span style="font-size:11px;color:${muted};">Secured by</span>` +
|