@manyrows/appkit-react 0.1.5 → 0.1.6

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.d.cts CHANGED
@@ -12,8 +12,6 @@ type ManyRowsAppKitReady = {
12
12
  containerId?: string;
13
13
  workspace: string;
14
14
  appId: string;
15
- project: string;
16
- env: string;
17
15
  baseURL: string;
18
16
  version: string;
19
17
  };
@@ -55,10 +53,14 @@ declare function AppKitAuthed(props: {
55
53
  */
56
54
  fallback?: React.ReactNode;
57
55
  }): react_jsx_runtime.JSX.Element;
56
+ type AppKitTheme = {
57
+ primaryColor?: string;
58
+ };
58
59
  type AppKitProps = {
59
60
  workspace: string;
60
61
  appId: string;
61
62
  baseURL?: string;
63
+ theme?: AppKitTheme;
62
64
  src?: string;
63
65
  timeoutMs?: number;
64
66
  silent?: boolean;
@@ -89,4 +91,9 @@ type AppKitProps = {
89
91
  };
90
92
  declare function AppKit(props: AppKitProps): react_jsx_runtime.JSX.Element;
91
93
 
92
- export { AppKit, AppKitAuthed, type ManyRowsAppKitError, type ManyRowsAppKitErrorCode, type ManyRowsAppKitHandle, type ManyRowsAppKitReady, type ManyRowsAppKitSnapshot, useAppKit };
94
+ interface UserButtonProps {
95
+ size?: "small" | "medium" | "large";
96
+ }
97
+ declare function UserButton({ size }: UserButtonProps): react_jsx_runtime.JSX.Element;
98
+
99
+ export { AppKit, AppKitAuthed, type AppKitTheme, type ManyRowsAppKitError, type ManyRowsAppKitErrorCode, type ManyRowsAppKitHandle, type ManyRowsAppKitReady, type ManyRowsAppKitSnapshot, UserButton, type UserButtonProps, useAppKit };
package/dist/index.d.ts CHANGED
@@ -12,8 +12,6 @@ type ManyRowsAppKitReady = {
12
12
  containerId?: string;
13
13
  workspace: string;
14
14
  appId: string;
15
- project: string;
16
- env: string;
17
15
  baseURL: string;
18
16
  version: string;
19
17
  };
@@ -55,10 +53,14 @@ declare function AppKitAuthed(props: {
55
53
  */
56
54
  fallback?: React.ReactNode;
57
55
  }): react_jsx_runtime.JSX.Element;
56
+ type AppKitTheme = {
57
+ primaryColor?: string;
58
+ };
58
59
  type AppKitProps = {
59
60
  workspace: string;
60
61
  appId: string;
61
62
  baseURL?: string;
63
+ theme?: AppKitTheme;
62
64
  src?: string;
63
65
  timeoutMs?: number;
64
66
  silent?: boolean;
@@ -89,4 +91,9 @@ type AppKitProps = {
89
91
  };
90
92
  declare function AppKit(props: AppKitProps): react_jsx_runtime.JSX.Element;
91
93
 
92
- export { AppKit, AppKitAuthed, type ManyRowsAppKitError, type ManyRowsAppKitErrorCode, type ManyRowsAppKitHandle, type ManyRowsAppKitReady, type ManyRowsAppKitSnapshot, useAppKit };
94
+ interface UserButtonProps {
95
+ size?: "small" | "medium" | "large";
96
+ }
97
+ declare function UserButton({ size }: UserButtonProps): react_jsx_runtime.JSX.Element;
98
+
99
+ export { AppKit, AppKitAuthed, type AppKitTheme, type ManyRowsAppKitError, type ManyRowsAppKitErrorCode, type ManyRowsAppKitHandle, type ManyRowsAppKitReady, type ManyRowsAppKitSnapshot, UserButton, type UserButtonProps, useAppKit };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { createContext, useContext, useId, useMemo, useRef, useState, useEffect } from 'react';
1
+ import React, { createContext, useContext, useId, useMemo, useRef, useState, useEffect, useCallback } from 'react';
2
2
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
3
 
4
4
  // src/AppKit.tsx
@@ -277,6 +277,7 @@ function AppKit(props) {
277
277
  workspace: props.workspace.trim(),
278
278
  appId: props.appId.trim(),
279
279
  baseURL: props.baseURL,
280
+ theme: props.theme,
280
281
  silent: props.silent,
281
282
  throwOnError: props.throwOnError,
282
283
  // ✅ If host provides children, hide runtime default authed UI.
@@ -373,7 +374,439 @@ function AppKit(props) {
373
374
  /* @__PURE__ */ jsx("div", { id: containerId })
374
375
  ] }) });
375
376
  }
377
+ function Toast({ toast, onClose }) {
378
+ useEffect(() => {
379
+ if (toast) {
380
+ const timer = setTimeout(onClose, 4e3);
381
+ return () => clearTimeout(timer);
382
+ }
383
+ }, [toast, onClose]);
384
+ if (!toast) return null;
385
+ return /* @__PURE__ */ jsxs(
386
+ "div",
387
+ {
388
+ style: {
389
+ position: "fixed",
390
+ bottom: 24,
391
+ left: "50%",
392
+ transform: "translateX(-50%)",
393
+ padding: "12px 20px",
394
+ borderRadius: 8,
395
+ backgroundColor: toast.type === "success" ? "#2e7d32" : "#d32f2f",
396
+ color: "#fff",
397
+ fontSize: 14,
398
+ fontWeight: 500,
399
+ boxShadow: "0 4px 12px rgba(0,0,0,0.2)",
400
+ zIndex: 1e4,
401
+ display: "flex",
402
+ alignItems: "center",
403
+ gap: 12
404
+ },
405
+ children: [
406
+ toast.message,
407
+ /* @__PURE__ */ jsx(
408
+ "button",
409
+ {
410
+ onClick: onClose,
411
+ style: {
412
+ background: "none",
413
+ border: "none",
414
+ color: "#fff",
415
+ cursor: "pointer",
416
+ fontSize: 18,
417
+ lineHeight: 1,
418
+ padding: 0,
419
+ opacity: 0.8
420
+ },
421
+ children: "\xD7"
422
+ }
423
+ )
424
+ ]
425
+ }
426
+ );
427
+ }
428
+ function getInitials(name, email) {
429
+ if (name) {
430
+ const parts = name.trim().split(/\s+/);
431
+ if (parts.length >= 2) {
432
+ return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
433
+ }
434
+ return name.slice(0, 2).toUpperCase();
435
+ }
436
+ if (email) {
437
+ return email.slice(0, 2).toUpperCase();
438
+ }
439
+ return "?";
440
+ }
441
+ function stringToColor(str) {
442
+ let hash = 0;
443
+ for (let i = 0; i < str.length; i++) {
444
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
445
+ }
446
+ const colors = [
447
+ "#1976d2",
448
+ "#388e3c",
449
+ "#d32f2f",
450
+ "#7b1fa2",
451
+ "#c2185b",
452
+ "#0288d1",
453
+ "#00796b",
454
+ "#f57c00"
455
+ ];
456
+ return colors[Math.abs(hash) % colors.length];
457
+ }
458
+ function UserButton({ size = "medium" }) {
459
+ const { snapshot, logout, refresh, readyInfo } = useAppKit();
460
+ const [open, setOpen] = useState(false);
461
+ const [loggingOut, setLoggingOut] = useState(false);
462
+ const account = snapshot?.appData?.account;
463
+ const initials = getInitials(account?.name, account?.email);
464
+ const avatarColor = stringToColor(account?.email || "user");
465
+ const sizeMap = { small: 32, medium: 40, large: 48 };
466
+ const fontSizeMap = { small: 14, medium: 16, large: 20 };
467
+ const avatarSize = sizeMap[size];
468
+ const handleLogout = async () => {
469
+ setLoggingOut(true);
470
+ try {
471
+ await logout();
472
+ } finally {
473
+ setLoggingOut(false);
474
+ setOpen(false);
475
+ }
476
+ };
477
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
478
+ /* @__PURE__ */ jsx(
479
+ "button",
480
+ {
481
+ onClick: () => setOpen(true),
482
+ "aria-label": "Open profile",
483
+ style: {
484
+ display: "flex",
485
+ alignItems: "center",
486
+ justifyContent: "center",
487
+ width: avatarSize,
488
+ height: avatarSize,
489
+ borderRadius: "50%",
490
+ backgroundColor: avatarColor,
491
+ color: "#fff",
492
+ border: "none",
493
+ cursor: "pointer",
494
+ fontSize: fontSizeMap[size],
495
+ fontWeight: 600,
496
+ fontFamily: "inherit",
497
+ padding: 0
498
+ },
499
+ children: initials
500
+ }
501
+ ),
502
+ open && /* @__PURE__ */ jsx(
503
+ ProfileModal,
504
+ {
505
+ account,
506
+ onClose: () => setOpen(false),
507
+ onLogout: handleLogout,
508
+ loggingOut,
509
+ avatarColor,
510
+ initials,
511
+ baseURL: readyInfo?.baseURL,
512
+ jwtToken: snapshot?.jwtToken,
513
+ onNameUpdated: refresh
514
+ }
515
+ )
516
+ ] });
517
+ }
518
+ function ProfileModal({
519
+ account,
520
+ onClose,
521
+ onLogout,
522
+ loggingOut,
523
+ avatarColor,
524
+ initials,
525
+ baseURL,
526
+ jwtToken,
527
+ onNameUpdated
528
+ }) {
529
+ const [editing, setEditing] = useState(false);
530
+ const [editName, setEditName] = useState(account?.name || "");
531
+ const [saving, setSaving] = useState(false);
532
+ const [error, setError] = useState(null);
533
+ const [toast, setToast] = useState(null);
534
+ const clearToast = useCallback(() => setToast(null), []);
535
+ useEffect(() => {
536
+ setEditName(account?.name || "");
537
+ setEditing(false);
538
+ setError(null);
539
+ }, [account?.name]);
540
+ const handleSave = async () => {
541
+ const trimmed = editName.trim();
542
+ if (!trimmed) {
543
+ setError("Display name is required");
544
+ return;
545
+ }
546
+ if (trimmed.length > 200) {
547
+ setError("Display name is too long");
548
+ return;
549
+ }
550
+ if (trimmed === account?.name) {
551
+ setEditing(false);
552
+ return;
553
+ }
554
+ setSaving(true);
555
+ setError(null);
556
+ try {
557
+ const res = await fetch(`${baseURL}/a/profile/display-name`, {
558
+ method: "POST",
559
+ headers: {
560
+ Authorization: `Bearer ${jwtToken}`,
561
+ "Content-Type": "application/json"
562
+ },
563
+ body: JSON.stringify({ displayName: trimmed })
564
+ });
565
+ if (!res.ok) {
566
+ const text = await res.text();
567
+ throw new Error(text || "Failed to update name");
568
+ }
569
+ setEditing(false);
570
+ onNameUpdated();
571
+ setToast({ message: "Display name updated", type: "success" });
572
+ } catch (err) {
573
+ const msg = err.message || "Failed to update name";
574
+ setError(msg);
575
+ setToast({ message: msg, type: "error" });
576
+ } finally {
577
+ setSaving(false);
578
+ }
579
+ };
580
+ const handleKeyDown = (e) => {
581
+ if (e.key === "Enter") {
582
+ e.preventDefault();
583
+ handleSave();
584
+ } else if (e.key === "Escape") {
585
+ setEditing(false);
586
+ setEditName(account?.name || "");
587
+ setError(null);
588
+ }
589
+ };
590
+ return /* @__PURE__ */ jsxs(
591
+ "div",
592
+ {
593
+ style: {
594
+ position: "fixed",
595
+ inset: 0,
596
+ zIndex: 9999,
597
+ display: "flex",
598
+ alignItems: "center",
599
+ justifyContent: "center"
600
+ },
601
+ children: [
602
+ /* @__PURE__ */ jsx(
603
+ "div",
604
+ {
605
+ onClick: onClose,
606
+ style: {
607
+ position: "absolute",
608
+ inset: 0,
609
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
610
+ }
611
+ }
612
+ ),
613
+ /* @__PURE__ */ jsxs(
614
+ "div",
615
+ {
616
+ style: {
617
+ position: "relative",
618
+ backgroundColor: "#fff",
619
+ borderRadius: 12,
620
+ boxShadow: "0 8px 32px rgba(0,0,0,0.2)",
621
+ width: "100%",
622
+ maxWidth: 360,
623
+ margin: 16,
624
+ overflow: "hidden"
625
+ },
626
+ children: [
627
+ /* @__PURE__ */ jsxs(
628
+ "div",
629
+ {
630
+ style: {
631
+ display: "flex",
632
+ alignItems: "center",
633
+ justifyContent: "space-between",
634
+ padding: "16px 20px",
635
+ borderBottom: "1px solid #eee"
636
+ },
637
+ children: [
638
+ /* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 18, fontWeight: 600 }, children: "Profile" }),
639
+ /* @__PURE__ */ jsx(
640
+ "button",
641
+ {
642
+ onClick: onClose,
643
+ style: {
644
+ background: "none",
645
+ border: "none",
646
+ cursor: "pointer",
647
+ padding: 4,
648
+ fontSize: 20,
649
+ lineHeight: 1,
650
+ color: "#666"
651
+ },
652
+ children: "\xD7"
653
+ }
654
+ )
655
+ ]
656
+ }
657
+ ),
658
+ /* @__PURE__ */ jsxs("div", { style: { padding: 20 }, children: [
659
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 16, marginBottom: 20 }, children: [
660
+ /* @__PURE__ */ jsx(
661
+ "div",
662
+ {
663
+ style: {
664
+ width: 64,
665
+ height: 64,
666
+ borderRadius: "50%",
667
+ backgroundColor: avatarColor,
668
+ color: "#fff",
669
+ display: "flex",
670
+ alignItems: "center",
671
+ justifyContent: "center",
672
+ fontSize: 24,
673
+ fontWeight: 600,
674
+ flexShrink: 0
675
+ },
676
+ children: initials
677
+ }
678
+ ),
679
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
680
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 18, fontWeight: 600, marginBottom: 2 }, children: account?.name || "User" }),
681
+ /* @__PURE__ */ jsx(
682
+ "div",
683
+ {
684
+ style: {
685
+ fontSize: 14,
686
+ color: "#666",
687
+ overflow: "hidden",
688
+ textOverflow: "ellipsis",
689
+ whiteSpace: "nowrap"
690
+ },
691
+ children: account?.email
692
+ }
693
+ )
694
+ ] })
695
+ ] }),
696
+ /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #eee", margin: "16px 0" } }),
697
+ error && /* @__PURE__ */ jsx(
698
+ "div",
699
+ {
700
+ style: {
701
+ padding: "10px 14px",
702
+ marginBottom: 16,
703
+ backgroundColor: "#ffebee",
704
+ color: "#c62828",
705
+ borderRadius: 6,
706
+ fontSize: 14
707
+ },
708
+ children: error
709
+ }
710
+ ),
711
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
712
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }, children: [
713
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: "#888", fontWeight: 600 }, children: "Display Name" }),
714
+ !editing && /* @__PURE__ */ jsx(
715
+ "button",
716
+ {
717
+ onClick: () => setEditing(true),
718
+ style: {
719
+ background: "none",
720
+ border: "none",
721
+ cursor: "pointer",
722
+ padding: 2,
723
+ fontSize: 12,
724
+ color: "#1976d2"
725
+ },
726
+ children: "Edit"
727
+ }
728
+ )
729
+ ] }),
730
+ editing ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
731
+ /* @__PURE__ */ jsx(
732
+ "input",
733
+ {
734
+ type: "text",
735
+ value: editName,
736
+ onChange: (e) => setEditName(e.target.value),
737
+ onKeyDown: handleKeyDown,
738
+ disabled: saving,
739
+ autoFocus: true,
740
+ placeholder: "Enter display name",
741
+ style: {
742
+ flex: 1,
743
+ padding: "8px 12px",
744
+ fontSize: 14,
745
+ border: `1px solid ${error ? "#d32f2f" : "#ddd"}`,
746
+ borderRadius: 6,
747
+ outline: "none"
748
+ }
749
+ }
750
+ ),
751
+ /* @__PURE__ */ jsx(
752
+ "button",
753
+ {
754
+ onClick: handleSave,
755
+ disabled: saving,
756
+ style: {
757
+ padding: "8px 14px",
758
+ fontSize: 14,
759
+ backgroundColor: "#1976d2",
760
+ color: "#fff",
761
+ border: "none",
762
+ borderRadius: 6,
763
+ cursor: saving ? "not-allowed" : "pointer",
764
+ opacity: saving ? 0.6 : 1
765
+ },
766
+ children: saving ? "\u2026" : "Save"
767
+ }
768
+ )
769
+ ] }) : /* @__PURE__ */ jsx("div", { style: { fontSize: 15 }, children: account?.name || "\u2014" })
770
+ ] }),
771
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
772
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: "#888", fontWeight: 600, marginBottom: 4 }, children: "Email" }),
773
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 15 }, children: account?.email || "\u2014" })
774
+ ] }),
775
+ /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #eee", margin: "16px 0" } }),
776
+ /* @__PURE__ */ jsx(
777
+ "button",
778
+ {
779
+ onClick: onLogout,
780
+ disabled: loggingOut,
781
+ style: {
782
+ width: "100%",
783
+ padding: "10px 16px",
784
+ fontSize: 15,
785
+ fontWeight: 500,
786
+ color: "#d32f2f",
787
+ backgroundColor: "#fff",
788
+ border: "1px solid #d32f2f",
789
+ borderRadius: 8,
790
+ cursor: loggingOut ? "not-allowed" : "pointer",
791
+ opacity: loggingOut ? 0.6 : 1,
792
+ display: "flex",
793
+ alignItems: "center",
794
+ justifyContent: "center",
795
+ gap: 8
796
+ },
797
+ children: loggingOut ? "Logging out\u2026" : "Log out"
798
+ }
799
+ )
800
+ ] })
801
+ ]
802
+ }
803
+ ),
804
+ /* @__PURE__ */ jsx(Toast, { toast, onClose: clearToast })
805
+ ]
806
+ }
807
+ );
808
+ }
376
809
 
377
- export { AppKit, AppKitAuthed, useAppKit };
810
+ export { AppKit, AppKitAuthed, UserButton, useAppKit };
378
811
  //# sourceMappingURL=index.js.map
379
812
  //# sourceMappingURL=index.js.map