@brdgapp/ui 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.md +39 -85
  3. package/components/action-menu.d.ts +65 -0
  4. package/components/action-menu.js +48 -0
  5. package/components/avatar.d.ts +50 -0
  6. package/components/avatar.js +91 -0
  7. package/components/badge.d.ts +12 -0
  8. package/components/badge.js +23 -0
  9. package/components/bottom-sheet.d.ts +17 -0
  10. package/components/bottom-sheet.js +12 -0
  11. package/components/brand-mark.d.ts +20 -0
  12. package/components/brand-mark.js +44 -0
  13. package/components/button-group.d.ts +25 -0
  14. package/components/button-group.js +53 -0
  15. package/components/button.d.ts +33 -0
  16. package/components/button.js +82 -0
  17. package/components/card.d.ts +78 -0
  18. package/components/card.js +75 -0
  19. package/components/checkbox.d.ts +22 -0
  20. package/components/checkbox.js +35 -0
  21. package/components/collapsible.d.ts +29 -0
  22. package/components/collapsible.js +38 -0
  23. package/components/column-filter-menu.d.ts +15 -0
  24. package/components/column-filter-menu.js +30 -0
  25. package/components/column-sort-button.d.ts +18 -0
  26. package/components/column-sort-button.js +14 -0
  27. package/components/confirm-dialog.d.ts +20 -0
  28. package/components/confirm-dialog.js +17 -0
  29. package/components/copy-field.d.ts +17 -0
  30. package/components/copy-field.js +36 -0
  31. package/components/data-state.d.ts +44 -0
  32. package/components/data-state.js +25 -0
  33. package/components/data-table.d.ts +65 -0
  34. package/components/data-table.js +84 -0
  35. package/components/dialog.d.ts +19 -0
  36. package/components/dialog.js +10 -0
  37. package/components/drawer.d.ts +21 -0
  38. package/components/drawer.js +17 -0
  39. package/components/expandable-bio.d.ts +8 -0
  40. package/components/expandable-bio.js +26 -0
  41. package/components/field.d.ts +29 -0
  42. package/components/field.js +27 -0
  43. package/components/filter-chip.d.ts +11 -0
  44. package/components/filter-chip.js +8 -0
  45. package/components/filter-pill.d.ts +34 -0
  46. package/components/filter-pill.js +35 -0
  47. package/components/hover-card.d.ts +21 -0
  48. package/components/hover-card.js +20 -0
  49. package/components/icon-button.d.ts +16 -0
  50. package/components/icon-button.js +32 -0
  51. package/components/illustration.d.ts +33 -0
  52. package/components/illustration.js +70 -0
  53. package/components/info-hint.d.ts +29 -0
  54. package/components/info-hint.js +42 -0
  55. package/components/input.d.ts +9 -0
  56. package/components/input.js +15 -0
  57. package/components/network-graph.d.ts +48 -0
  58. package/components/network-graph.js +37 -0
  59. package/components/page-banner.d.ts +20 -0
  60. package/components/page-banner.js +29 -0
  61. package/components/scene.d.ts +12 -0
  62. package/components/scene.js +7 -0
  63. package/components/search-input.d.ts +8 -0
  64. package/components/search-input.js +14 -0
  65. package/components/segmented-control.d.ts +18 -0
  66. package/components/segmented-control.js +14 -0
  67. package/components/skeleton.d.ts +12 -0
  68. package/components/skeleton.js +21 -0
  69. package/components/skip-link.d.ts +8 -0
  70. package/components/skip-link.js +7 -0
  71. package/components/stat-box.d.ts +23 -0
  72. package/components/stat-box.js +29 -0
  73. package/components/switch.d.ts +13 -0
  74. package/components/switch.js +8 -0
  75. package/components/tabs.d.ts +6 -0
  76. package/components/tabs.js +25 -0
  77. package/components/tag-input.d.ts +15 -0
  78. package/components/tag-input.js +40 -0
  79. package/components/tag.d.ts +27 -0
  80. package/components/tag.js +35 -0
  81. package/components/textarea.d.ts +12 -0
  82. package/components/textarea.js +78 -0
  83. package/components/theme-switch.d.ts +6 -0
  84. package/components/theme-switch.js +16 -0
  85. package/components/toast.d.ts +25 -0
  86. package/components/toast.js +44 -0
  87. package/hooks/use-is-mobile.d.ts +2 -0
  88. package/hooks/use-is-mobile.js +16 -0
  89. package/hooks/use-mock-query.d.ts +23 -0
  90. package/hooks/use-mock-query.js +36 -0
  91. package/hooks/use-theme.d.ts +11 -0
  92. package/hooks/use-theme.js +34 -0
  93. package/lib/demo-controls.d.ts +24 -0
  94. package/lib/demo-controls.js +24 -0
  95. package/lib/format.d.ts +17 -0
  96. package/lib/format.js +55 -0
  97. package/lib/layout.d.ts +19 -0
  98. package/lib/layout.js +24 -0
  99. package/lib/motion.d.ts +16 -0
  100. package/lib/motion.js +18 -0
  101. package/lib/theme.d.ts +21 -0
  102. package/lib/theme.js +63 -0
  103. package/lib/utils.d.ts +22 -0
  104. package/lib/utils.js +27 -0
  105. package/package.json +32 -16
  106. package/styles/globals.css +595 -0
  107. package/src/index.ts +0 -18
  108. package/src/network-people-helper/README.md +0 -130
  109. package/src/network-people-helper/api.ts +0 -174
  110. package/src/network-people-helper/contact-avatar.tsx +0 -63
  111. package/src/network-people-helper/domain.ts +0 -14
  112. package/src/network-people-helper/icons.tsx +0 -130
  113. package/src/network-people-helper/index.ts +0 -6
  114. package/src/network-people-helper/network-people-helper.tsx +0 -397
  115. package/src/network-people-helper/network-people-nudge.tsx +0 -98
  116. package/src/network-people-helper/open-helper.ts +0 -22
  117. package/src/network-people-helper/person-menu.tsx +0 -211
  118. package/src/network-people-helper/styles.css +0 -848
  119. package/src/network-people-helper/types.ts +0 -23
  120. package/src/network-people-helper/use-network-people-list.ts +0 -122
  121. package/src/network-people-helper/use-network-preview.ts +0 -43
  122. package/src/provider.tsx +0 -59
  123. package/src/styles.css +0 -1
@@ -1,211 +0,0 @@
1
- "use client"
2
-
3
- import { useEffect, useLayoutEffect, useRef, useState } from "react"
4
- import { createPortal } from "react-dom"
5
- import { CheckIcon, CopyIcon, LinkIcon, MoreIcon, UserIcon, UserPlusIcon } from "./icons"
6
- import type { BridgeClientConfig } from "../provider"
7
- import type { NetworkContact, NetworkPersonAction } from "./types"
8
-
9
- function introUrl(inboxUrl: string, email: string): string {
10
- const url = new URL(inboxUrl)
11
- url.searchParams.set("action", "make-intro")
12
- url.searchParams.set("n1", email)
13
- return url.toString()
14
- }
15
-
16
- export function PersonMenu({
17
- contact,
18
- customActions,
19
- config,
20
- }: {
21
- contact: NetworkContact
22
- customActions: NetworkPersonAction[]
23
- config: BridgeClientConfig
24
- }) {
25
- const [open, setOpen] = useState(false)
26
- const [copied, setCopied] = useState(false)
27
- const buttonRef = useRef<HTMLButtonElement>(null)
28
- const menuRef = useRef<HTMLDivElement>(null)
29
- const [position, setPosition] = useState<{ top: number; left: number } | null>(null)
30
- const profileUrl = contact.username ? config.profileUrlForUsername(contact.username) : null
31
- const makeIntro = contact.email ? introUrl(config.introInboxUrl, contact.email) : null
32
- const hasActions = customActions.length > 0 || Boolean(makeIntro || contact.shareLink || profileUrl)
33
-
34
- useLayoutEffect(() => {
35
- if (!open || !buttonRef.current) return
36
- function place() {
37
- const rect = buttonRef.current?.getBoundingClientRect()
38
- if (!rect) return
39
- const width = 248
40
- const left = Math.max(8, Math.min(rect.right - width, window.innerWidth - width - 8))
41
- const estimatedHeight = menuRef.current?.offsetHeight ?? 180
42
- const below = rect.bottom + 6
43
- const top = below + estimatedHeight > window.innerHeight - 8 ? Math.max(8, rect.top - estimatedHeight - 6) : below
44
- setPosition({ top, left })
45
- }
46
- place()
47
- window.addEventListener("resize", place)
48
- window.addEventListener("scroll", place, true)
49
- return () => {
50
- window.removeEventListener("resize", place)
51
- window.removeEventListener("scroll", place, true)
52
- }
53
- }, [open])
54
-
55
- useEffect(() => {
56
- if (!open) return
57
- function onPointer(event: MouseEvent) {
58
- const target = event.target
59
- if (!(target instanceof Node)) return
60
- if (buttonRef.current?.contains(target) || menuRef.current?.contains(target)) return
61
- setOpen(false)
62
- setCopied(false)
63
- }
64
- function onKey(event: KeyboardEvent) {
65
- if (event.key === "Escape") {
66
- setOpen(false)
67
- setCopied(false)
68
- }
69
- }
70
- document.addEventListener("mousedown", onPointer)
71
- document.addEventListener("keydown", onKey)
72
- return () => {
73
- document.removeEventListener("mousedown", onPointer)
74
- document.removeEventListener("keydown", onKey)
75
- }
76
- }, [open])
77
-
78
- if (!hasActions) return null
79
-
80
- async function copyIntroLink() {
81
- if (!contact.shareLink) return
82
- try {
83
- await navigator.clipboard.writeText(contact.shareLink)
84
- setCopied(true)
85
- window.setTimeout(() => {
86
- setCopied(false)
87
- setOpen(false)
88
- }, 1200)
89
- } catch {
90
- // Clipboard can fail in restricted contexts.
91
- }
92
- }
93
-
94
- const menu = open ? (
95
- <div
96
- ref={menuRef}
97
- className="bridge-network-helper__person-menu-content"
98
- style={position ? { top: position.top, left: position.left } : { visibility: "hidden" }}
99
- role="menu"
100
- onClick={(event) => event.stopPropagation()}
101
- >
102
- {customActions.map((action) => (
103
- <button
104
- key={action.id}
105
- type="button"
106
- role="menuitem"
107
- className={
108
- action.description
109
- ? "bridge-network-helper__person-menu-item has-description"
110
- : "bridge-network-helper__person-menu-item"
111
- }
112
- onClick={() => {
113
- setOpen(false)
114
- action.onSelect(contact)
115
- }}
116
- >
117
- {action.icon ? <span className="bridge-network-helper__person-menu-icon">{action.icon}</span> : null}
118
- <span>
119
- <span className="bridge-network-helper__person-menu-label">{action.label}</span>
120
- {action.description ? (
121
- <span className="bridge-network-helper__person-menu-description">{action.description}</span>
122
- ) : null}
123
- </span>
124
- </button>
125
- ))}
126
- {makeIntro ? (
127
- <a className="bridge-network-helper__person-menu-item" role="menuitem" href={makeIntro} target="_blank" rel="noopener noreferrer">
128
- <UserPlusIcon className="bridge-network-helper__person-menu-icon" />
129
- Make intro
130
- </a>
131
- ) : null}
132
- {contact.shareLink ? (
133
- <button
134
- type="button"
135
- role="menuitem"
136
- className="bridge-network-helper__person-menu-item"
137
- onClick={() => {
138
- if (!copied) void copyIntroLink()
139
- }}
140
- >
141
- {copied ? (
142
- <CheckIcon className="bridge-network-helper__person-menu-icon is-copied" />
143
- ) : (
144
- <LinkIcon className="bridge-network-helper__person-menu-icon" />
145
- )}
146
- {copied ? "Copied" : "Copy intro link"}
147
- </button>
148
- ) : null}
149
- {profileUrl ? (
150
- <a className="bridge-network-helper__person-menu-item" role="menuitem" href={profileUrl} target="_blank" rel="noopener noreferrer">
151
- <UserIcon className="bridge-network-helper__person-menu-icon" />
152
- View Bridge profile
153
- </a>
154
- ) : null}
155
- </div>
156
- ) : null
157
-
158
- return (
159
- <>
160
- <button
161
- ref={buttonRef}
162
- type="button"
163
- className="bridge-network-helper__person-menu"
164
- aria-label={`More options for ${contact.name}`}
165
- aria-haspopup="menu"
166
- aria-expanded={open}
167
- data-state={open ? "open" : "closed"}
168
- onClick={(event) => {
169
- event.stopPropagation()
170
- setOpen((current) => !current)
171
- setCopied(false)
172
- }}
173
- >
174
- <MoreIcon />
175
- </button>
176
- {menu && typeof document !== "undefined" ? createPortal(menu, document.body) : null}
177
- </>
178
- )
179
- }
180
-
181
- export function ShareLinkCopyControl({ shareLink }: { shareLink: string }) {
182
- const [copied, setCopied] = useState(false)
183
- const display = shareLink.replace(/^https?:\/\//i, "")
184
-
185
- async function handleCopy() {
186
- try {
187
- await navigator.clipboard.writeText(shareLink)
188
- setCopied(true)
189
- window.setTimeout(() => setCopied(false), 1600)
190
- } catch {
191
- // Clipboard can fail in restricted contexts.
192
- }
193
- }
194
-
195
- return (
196
- <button
197
- type="button"
198
- className="bridge-network-helper__share-link"
199
- onClick={handleCopy}
200
- title={copied ? "Copied" : "Copy Introlink"}
201
- aria-label={copied ? "Introlink copied" : `Copy Introlink ${display}`}
202
- >
203
- <span className="bridge-network-helper__share-link-text">{display}</span>
204
- {copied ? (
205
- <CheckIcon className="bridge-network-helper__share-link-icon is-copied" />
206
- ) : (
207
- <CopyIcon className="bridge-network-helper__share-link-icon" />
208
- )}
209
- </button>
210
- )
211
- }