@orangecheck/auth-client 0.3.0 → 0.4.1

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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import * as React2 from 'react';
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // src/provider.tsx
@@ -20,7 +20,7 @@ function buildSignInUrl(cfg, returnTo) {
20
20
  u.searchParams.set("return_to", returnTo);
21
21
  return u.toString();
22
22
  }
23
- var SessionContext = React.createContext(null);
23
+ var SessionContext = React2.createContext(null);
24
24
  function normalizeAccount(raw) {
25
25
  if (!raw) return null;
26
26
  const address = raw.btc_address ?? raw.address;
@@ -38,11 +38,11 @@ function OcSessionProvider({
38
38
  config,
39
39
  defaultReturnTo
40
40
  }) {
41
- const cfg = React.useMemo(() => resolveConfig(config), [config]);
42
- const [account, setAccount] = React.useState(null);
43
- const [status, setStatus] = React.useState("loading");
44
- const [error, setError] = React.useState(null);
45
- const refresh = React.useCallback(async () => {
41
+ const cfg = React2.useMemo(() => resolveConfig(config), [config]);
42
+ const [account, setAccount] = React2.useState(null);
43
+ const [status, setStatus] = React2.useState("loading");
44
+ const [error, setError] = React2.useState(null);
45
+ const refresh = React2.useCallback(async () => {
46
46
  if (typeof window === "undefined") return;
47
47
  try {
48
48
  const res = await fetch(cfg.mePath, {
@@ -71,10 +71,10 @@ function OcSessionProvider({
71
71
  setError(err instanceof Error ? err : new Error(String(err)));
72
72
  }
73
73
  }, [cfg.mePath]);
74
- React.useEffect(() => {
74
+ React2.useEffect(() => {
75
75
  void refresh();
76
76
  }, [refresh]);
77
- const signOut = React.useCallback(async () => {
77
+ const signOut = React2.useCallback(async () => {
78
78
  try {
79
79
  await fetch(`${cfg.authOrigin}${cfg.logoutPath}`, {
80
80
  method: "POST",
@@ -85,7 +85,7 @@ function OcSessionProvider({
85
85
  setAccount(null);
86
86
  setStatus("anonymous");
87
87
  }, [cfg.authOrigin, cfg.logoutPath]);
88
- const value = React.useMemo(() => {
88
+ const value = React2.useMemo(() => {
89
89
  const returnTo = defaultReturnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
90
90
  return {
91
91
  status,
@@ -99,7 +99,7 @@ function OcSessionProvider({
99
99
  return /* @__PURE__ */ jsx(SessionContext.Provider, { value, children });
100
100
  }
101
101
  function useOcSession() {
102
- const ctx = React.useContext(SessionContext);
102
+ const ctx = React2.useContext(SessionContext);
103
103
  if (!ctx) {
104
104
  throw new Error(
105
105
  "[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>"
@@ -108,7 +108,7 @@ function useOcSession() {
108
108
  return ctx;
109
109
  }
110
110
  function useOptionalOcSession() {
111
- return React.useContext(SessionContext);
111
+ return React2.useContext(SessionContext);
112
112
  }
113
113
  function shortenAddress(addr) {
114
114
  if (addr.length <= 12) return addr;
@@ -123,9 +123,270 @@ function isPrefixOf(value, target) {
123
123
  }
124
124
  var listboxIdCounter = 0;
125
125
  function useUniqueId(prefix) {
126
- const [id] = React.useState(() => `${prefix}-${++listboxIdCounter}`);
126
+ const [id] = React2.useState(() => `${prefix}-${++listboxIdCounter}`);
127
127
  return id;
128
128
  }
129
+ function OcAccountChip({
130
+ dashboardUrl = "https://ochk.io/dashboard",
131
+ signInLabel = "sign in",
132
+ className,
133
+ triggerClassName,
134
+ popoverClassName,
135
+ menuItemClassName
136
+ }) {
137
+ const { status, account, signInUrl, signOut } = useOcSession();
138
+ const [open, setOpen] = React2.useState(false);
139
+ const wrapRef = React2.useRef(null);
140
+ React2.useEffect(() => {
141
+ if (!open) return;
142
+ function handleClickOutside(e) {
143
+ if (wrapRef.current && !wrapRef.current.contains(e.target)) {
144
+ setOpen(false);
145
+ }
146
+ }
147
+ function handleEscape(e) {
148
+ if (e.key === "Escape") setOpen(false);
149
+ }
150
+ document.addEventListener("mousedown", handleClickOutside);
151
+ document.addEventListener("keydown", handleEscape);
152
+ return () => {
153
+ document.removeEventListener("mousedown", handleClickOutside);
154
+ document.removeEventListener("keydown", handleEscape);
155
+ };
156
+ }, [open]);
157
+ if (status === "loading") return null;
158
+ if (status !== "authenticated" || !account) {
159
+ return /* @__PURE__ */ jsx(
160
+ "a",
161
+ {
162
+ href: signInUrl,
163
+ className: triggerClassName ?? className,
164
+ "data-oc-account-chip-signin": "",
165
+ children: signInLabel
166
+ }
167
+ );
168
+ }
169
+ return /* @__PURE__ */ jsxs(
170
+ "div",
171
+ {
172
+ ref: wrapRef,
173
+ className,
174
+ "data-oc-account-chip": "",
175
+ style: { position: "relative", display: "inline-block" },
176
+ children: [
177
+ /* @__PURE__ */ jsxs(
178
+ "button",
179
+ {
180
+ type: "button",
181
+ onClick: () => setOpen((v) => !v),
182
+ "aria-haspopup": "menu",
183
+ "aria-expanded": open,
184
+ "aria-label": `Signed in as ${account.address}. Open account menu.`,
185
+ className: triggerClassName,
186
+ "data-oc-account-chip-trigger": "",
187
+ style: triggerClassName ? void 0 : {
188
+ display: "inline-flex",
189
+ alignItems: "center",
190
+ gap: "0.4rem",
191
+ padding: "0.25rem 0.625rem",
192
+ borderRadius: "9999px",
193
+ border: "1px solid var(--border, #27272a)",
194
+ background: "color-mix(in oklch, var(--muted, #27272a) 40%, transparent)",
195
+ color: "var(--foreground, #fafafa)",
196
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
197
+ fontSize: 12,
198
+ cursor: "pointer"
199
+ },
200
+ children: [
201
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { color: "#22c55e", fontSize: 8 }, children: "\u25CF" }),
202
+ /* @__PURE__ */ jsx("span", { children: shortenAddress(account.address) }),
203
+ /* @__PURE__ */ jsx(
204
+ "svg",
205
+ {
206
+ width: "9",
207
+ height: "9",
208
+ viewBox: "0 0 10 10",
209
+ "aria-hidden": "true",
210
+ style: {
211
+ transition: "transform 120ms",
212
+ transform: open ? "rotate(180deg)" : "rotate(0)",
213
+ opacity: 0.6
214
+ },
215
+ children: /* @__PURE__ */ jsx(
216
+ "path",
217
+ {
218
+ d: "M2 4 L5 7 L8 4",
219
+ stroke: "currentColor",
220
+ strokeWidth: "1.5",
221
+ fill: "none",
222
+ strokeLinecap: "round"
223
+ }
224
+ )
225
+ }
226
+ )
227
+ ]
228
+ }
229
+ ),
230
+ open && /* @__PURE__ */ jsxs(
231
+ "div",
232
+ {
233
+ role: "menu",
234
+ "aria-label": "Account menu",
235
+ className: popoverClassName,
236
+ "data-oc-account-chip-popover": "",
237
+ style: popoverClassName ? { position: "absolute", zIndex: 50, top: "calc(100% + 6px)", right: 0 } : {
238
+ position: "absolute",
239
+ zIndex: 50,
240
+ top: "calc(100% + 6px)",
241
+ right: 0,
242
+ minWidth: 240,
243
+ background: "var(--popover, #0a0a0a)",
244
+ color: "var(--popover-foreground, #fafafa)",
245
+ border: "1px solid var(--border, #27272a)",
246
+ boxShadow: "0 10px 32px -4px rgba(0,0,0,0.35)",
247
+ fontFamily: "ui-monospace, SFMono-Regular, monospace"
248
+ },
249
+ children: [
250
+ /* @__PURE__ */ jsxs(
251
+ "div",
252
+ {
253
+ "data-oc-account-chip-header": "",
254
+ style: {
255
+ padding: "0.75rem 0.75rem 0.625rem",
256
+ borderBottom: "1px solid var(--border, #27272a)"
257
+ },
258
+ children: [
259
+ /* @__PURE__ */ jsx(
260
+ "div",
261
+ {
262
+ "data-oc-account-chip-label": "",
263
+ style: {
264
+ color: "var(--primary, #f97316)",
265
+ fontSize: 10,
266
+ letterSpacing: "0.18em",
267
+ textTransform: "uppercase",
268
+ marginBottom: 4
269
+ },
270
+ children: "\xA7 signed in"
271
+ }
272
+ ),
273
+ /* @__PURE__ */ jsx(
274
+ "div",
275
+ {
276
+ "data-oc-account-chip-address": "",
277
+ style: {
278
+ color: "var(--popover-foreground, #fafafa)",
279
+ fontSize: 11,
280
+ wordBreak: "break-all",
281
+ lineHeight: 1.35
282
+ },
283
+ children: account.address
284
+ }
285
+ ),
286
+ account.displayName ? /* @__PURE__ */ jsx(
287
+ "div",
288
+ {
289
+ style: {
290
+ color: "var(--muted-foreground, #a1a1aa)",
291
+ fontSize: 10,
292
+ marginTop: 4,
293
+ letterSpacing: "0.06em"
294
+ },
295
+ children: account.displayName
296
+ }
297
+ ) : null
298
+ ]
299
+ }
300
+ ),
301
+ /* @__PURE__ */ jsxs("div", { role: "none", style: { padding: "0.25rem" }, children: [
302
+ dashboardUrl ? /* @__PURE__ */ jsxs(
303
+ "a",
304
+ {
305
+ role: "menuitem",
306
+ href: dashboardUrl,
307
+ onClick: () => setOpen(false),
308
+ className: menuItemClassName,
309
+ "data-oc-account-chip-item": "",
310
+ style: menuItemClassName ? void 0 : {
311
+ display: "flex",
312
+ alignItems: "center",
313
+ gap: "0.5rem",
314
+ padding: "0.5rem 0.625rem",
315
+ fontSize: 12,
316
+ color: "var(--popover-foreground, #fafafa)",
317
+ textDecoration: "none",
318
+ cursor: "pointer"
319
+ },
320
+ children: [
321
+ /* @__PURE__ */ jsx(
322
+ "span",
323
+ {
324
+ "aria-hidden": "true",
325
+ style: { color: "var(--muted-foreground, #a1a1aa)" },
326
+ children: "\u2192"
327
+ }
328
+ ),
329
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: "dashboard" }),
330
+ /* @__PURE__ */ jsx(
331
+ "span",
332
+ {
333
+ style: {
334
+ color: "var(--muted-foreground, #a1a1aa)",
335
+ fontSize: 10
336
+ },
337
+ children: "\u2197"
338
+ }
339
+ )
340
+ ]
341
+ }
342
+ ) : null,
343
+ /* @__PURE__ */ jsxs(
344
+ "button",
345
+ {
346
+ type: "button",
347
+ role: "menuitem",
348
+ onClick: () => {
349
+ setOpen(false);
350
+ void signOut();
351
+ },
352
+ className: menuItemClassName,
353
+ "data-oc-account-chip-item": "",
354
+ "data-oc-account-chip-signout": "",
355
+ style: menuItemClassName ? void 0 : {
356
+ display: "flex",
357
+ alignItems: "center",
358
+ gap: "0.5rem",
359
+ width: "100%",
360
+ padding: "0.5rem 0.625rem",
361
+ fontSize: 12,
362
+ color: "var(--popover-foreground, #fafafa)",
363
+ background: "transparent",
364
+ border: 0,
365
+ fontFamily: "inherit",
366
+ textAlign: "left",
367
+ cursor: "pointer"
368
+ },
369
+ children: [
370
+ /* @__PURE__ */ jsx(
371
+ "span",
372
+ {
373
+ "aria-hidden": "true",
374
+ style: { color: "var(--muted-foreground, #a1a1aa)" },
375
+ children: "\u2192"
376
+ }
377
+ ),
378
+ /* @__PURE__ */ jsx("span", { style: { flex: 1 }, children: "sign out" })
379
+ ]
380
+ }
381
+ )
382
+ ] })
383
+ ]
384
+ }
385
+ )
386
+ ]
387
+ }
388
+ );
389
+ }
129
390
  function OcSignInButton({
130
391
  label = "sign in with bitcoin",
131
392
  eager = false,
@@ -197,9 +458,9 @@ function useOcAddressSuggestion(options) {
197
458
  } = options;
198
459
  const { status, account } = useOcSession();
199
460
  const sessionAddress = status === "authenticated" ? account?.address ?? null : null;
200
- const [open, setOpen] = React.useState(false);
201
- const [highlighted, setHighlighted] = React.useState(false);
202
- const blurTimer = React.useRef(null);
461
+ const [open, setOpen] = React2.useState(false);
462
+ const [highlighted, setHighlighted] = React2.useState(false);
463
+ const blurTimer = React2.useRef(null);
203
464
  const listboxId = useUniqueId("oc-addr-listbox");
204
465
  const optionId = `${listboxId}-opt`;
205
466
  const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
@@ -211,7 +472,7 @@ function useOcAddressSuggestion(options) {
211
472
  setOpen(false);
212
473
  setHighlighted(false);
213
474
  }
214
- React.useEffect(() => {
475
+ React2.useEffect(() => {
215
476
  return () => {
216
477
  if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
217
478
  };
@@ -314,7 +575,7 @@ function useOcAddressSuggestion(options) {
314
575
  ) : null;
315
576
  return { inputProps, popover };
316
577
  }
317
- var OcAddressInput = React.forwardRef(
578
+ var OcAddressInput = React2.forwardRef(
318
579
  function OcAddressInput2({
319
580
  value,
320
581
  onValueChange,
@@ -329,15 +590,15 @@ var OcAddressInput = React.forwardRef(
329
590
  }, forwardedRef) {
330
591
  const { status, account } = useOcSession();
331
592
  const sessionAddress = status === "authenticated" ? account?.address ?? null : null;
332
- const [open, setOpen] = React.useState(false);
333
- const [highlighted, setHighlighted] = React.useState(false);
334
- const innerRef = React.useRef(null);
593
+ const [open, setOpen] = React2.useState(false);
594
+ const [highlighted, setHighlighted] = React2.useState(false);
595
+ const innerRef = React2.useRef(null);
335
596
  const setRef = (node) => {
336
597
  innerRef.current = node;
337
598
  if (typeof forwardedRef === "function") forwardedRef(node);
338
599
  else if (forwardedRef) forwardedRef.current = node;
339
600
  };
340
- const blurTimer = React.useRef(null);
601
+ const blurTimer = React2.useRef(null);
341
602
  const listboxId = useUniqueId("oc-addr-listbox");
342
603
  const optionId = `${listboxId}-opt`;
343
604
  const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
@@ -384,7 +645,7 @@ var OcAddressInput = React.forwardRef(
384
645
  }
385
646
  onKeyDown?.(e);
386
647
  }
387
- React.useEffect(() => {
648
+ React2.useEffect(() => {
388
649
  return () => {
389
650
  if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
390
651
  };
@@ -478,6 +739,6 @@ var OcAddressInput = React.forwardRef(
478
739
  }
479
740
  );
480
741
 
481
- export { DEFAULT_CONFIG, OcAccountPill, OcAddressInput, OcSessionProvider, OcSignInButton, buildSignInUrl, useOcAddressSuggestion, useOcSession, useOptionalOcSession };
742
+ export { DEFAULT_CONFIG, OcAccountChip, OcAccountPill, OcAddressInput, OcSessionProvider, OcSignInButton, buildSignInUrl, useOcAddressSuggestion, useOcSession, useOptionalOcSession };
482
743
  //# sourceMappingURL=index.mjs.map
483
744
  //# sourceMappingURL=index.mjs.map