@hanzo/rooms 0.1.50 → 0.1.51
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/Me.js +24 -6
- package/dist/Orgs.js +17 -6
- package/package.json +1 -1
package/dist/Me.js
CHANGED
|
@@ -24,7 +24,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
24
24
|
// `POST /v1/account/orgs` the no-organization gate uses (components/workspace/
|
|
25
25
|
// Orgs.tsx); the next load mints a token that carries it.
|
|
26
26
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
27
|
-
import { Popover, SizableText, View, XStack, YStack } from '@hanzo/gui';
|
|
27
|
+
import { Popover, SizableText, usePopperContextSlow, View, XStack, YStack } from '@hanzo/gui';
|
|
28
28
|
import { Building2, ChevronsUpDown, CreditCard, ExternalLink, Gauge, KeyRound, LifeBuoy, LogOut, Plus, Settings, Sparkles, Users, X } from 'lucide-react';
|
|
29
29
|
import { label, Meter, path, said as spoken, useStanding, useWho } from '@hanzo/build';
|
|
30
30
|
import { useOrganizations } from '@hanzo/iam/react';
|
|
@@ -132,6 +132,24 @@ function rove(e, close) {
|
|
|
132
132
|
e.preventDefault();
|
|
133
133
|
rows[to]?.focus();
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The menu grows once the plan is read (`Meter`), after it was placed, and the
|
|
137
|
+
* popover follows its anchor, not its own size. So it is placed again whenever
|
|
138
|
+
* its content changes size, and never hangs over the row it opens from.
|
|
139
|
+
*/
|
|
140
|
+
function Refit({ children }) {
|
|
141
|
+
const { update } = usePopperContextSlow();
|
|
142
|
+
const box = useRef(null);
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
const node = box.current;
|
|
145
|
+
if (!node || typeof ResizeObserver === 'undefined')
|
|
146
|
+
return;
|
|
147
|
+
const watch = new ResizeObserver(() => update());
|
|
148
|
+
watch.observe(node);
|
|
149
|
+
return () => watch.disconnect();
|
|
150
|
+
}, [update]);
|
|
151
|
+
return (_jsx(View, { render: _jsx("div", { ref: box }), self: "stretch", minW: 0, children: children }));
|
|
152
|
+
}
|
|
135
153
|
export function Me({ host, narrow = false, onOrg, }) {
|
|
136
154
|
const menu = useWho();
|
|
137
155
|
const [creating, setCreating] = useState(false);
|
|
@@ -168,11 +186,11 @@ export function Me({ host, narrow = false, onOrg, }) {
|
|
|
168
186
|
return null;
|
|
169
187
|
return (_jsxs(_Fragment, { children: [_jsxs(Popover, { open: menu.open, onOpenChange: menu.onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 8, children: [_jsx(Popover.Anchor, { width: "100%", children: _jsxs(XStack, { ref: (el) => {
|
|
170
188
|
row.current = el instanceof HTMLElement ? el : null;
|
|
171
|
-
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 30 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), standing ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: standing })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: _jsxs(YStack, { gap: 2, children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person.email || who }), _jsx(Rule, {}), _jsx(Meter, { read: read, onPlans: () => go(path({ kind: 'screen', screen: 'plans' })), onBilling: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(Rule, {}), _jsx(Label, { children: "Organizations" }), _jsx(YStack, { role: "group", "aria-label": "Organizations", gap: 2, children: all.map((o) => (_jsx(Item, { icon: _jsx(Mark, { org: o }), label: o.display, sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), checked: o.name === current, onPress: () => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
}, render: "button", "data-slot": "me", "aria-label": `Account: ${[who, current].filter(Boolean).join(' · ')}`, "aria-haspopup": "menu", "aria-expanded": menu.open, onPress: menu.toggle, width: narrow ? 40 : '100%', minH: narrow ? 40 : 48, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 30 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "left", children: who }), standing ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "left", children: standing })) : null] }), _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsx(Popover.Content, { ...glass(2), items: "stretch", borderWidth: 1, rounded: "$6", p: "$1.5", width: 288, maxW: "calc(100vw - 16px)", maxH: "calc(100dvh - 32px)", overflowY: "auto", overflowX: "hidden", children: _jsx(Refit, { children: _jsx(View, { render: _jsx("div", { ref: land, role: "menu", "aria-label": "Account", onKeyDown: (e) => rove(e, close) }), self: "stretch", minW: 0, children: _jsxs(YStack, { gap: 2, children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person.email || who }), _jsx(Rule, {}), _jsx(Meter, { read: read, onPlans: () => go(path({ kind: 'screen', screen: 'plans' })), onBilling: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(Rule, {}), _jsx(Label, { children: "Organizations" }), _jsx(YStack, { role: "group", "aria-label": "Organizations", gap: 2, children: all.map((o) => (_jsx(Item, { icon: _jsx(Mark, { org: o }), label: o.display, sub: [o.personal ? 'Personal' : said(o.role), planFor(o)].filter(Boolean).join(' · '), checked: o.name === current, onPress: () => {
|
|
190
|
+
menu.onOpenChange(false);
|
|
191
|
+
if (o.name !== current)
|
|
192
|
+
onOrg(o.name);
|
|
193
|
+
} }, o.name))) }), _jsx(Item, { icon: _jsx(Plus, { size: 15, "aria-hidden": true }), label: "Create organization", onPress: () => (menu.onOpenChange(false), setCreating(true)) }), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Organization settings", onPress: () => (menu.onOpenChange(false), window.location.assign(site(ORG_SETTINGS))) }), where().team ? null : here && !here.personal ? (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Open in Hanzo Team", trail: _jsx(ExternalLink, { size: 13, "aria-hidden": true, opacity: 0.6 }), onPress: () => (menu.onOpenChange(false), window.location.assign(`${ENTRY}&org=${encodeURIComponent(here.name)}`)) })) : (_jsx(Item, { icon: _jsx(Users, { size: 15, "aria-hidden": true }), label: "Create a team", onPress: () => (menu.onOpenChange(false), setCreating(true)) })), superAdmin() ? (_jsx(Item, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), label: "All organizations", onPress: () => (menu.onOpenChange(false), setSupporting(true)) })) : null, _jsx(Rule, {}), _jsx(Item, { icon: _jsx(Settings, { size: 15, "aria-hidden": true }), label: "Settings", onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(Item, { icon: _jsx(Gauge, { size: 15, "aria-hidden": true }), label: "Usage", onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(Item, { icon: _jsx(CreditCard, { size: 15, "aria-hidden": true }), label: "Billing", onPress: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(Item, { icon: _jsx(KeyRound, { size: 15, "aria-hidden": true }), label: "API keys", onPress: () => (menu.onOpenChange(false), window.location.assign(KEYS)) }), _jsx(Item, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), label: "View all plans", onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(Item, { icon: _jsx(LifeBuoy, { size: 15, "aria-hidden": true }), label: "Get help", onPress: () => (menu.onOpenChange(false), window.open(DOCS, '_blank', 'noopener,noreferrer')) }), host.signOut ? (_jsxs(_Fragment, { children: [_jsx(Rule, {}), _jsx(Item, { icon: _jsx(LogOut, { size: 15, "aria-hidden": true }), label: "Log out", onPress: () => (menu.onOpenChange(false), host.signOut?.()) })] })) : null] }) }) }) })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null] }));
|
|
176
194
|
}
|
|
177
195
|
/**
|
|
178
196
|
* The sheet that creates an organization: a name, then `POST /v1/account/orgs`,
|
package/dist/Orgs.js
CHANGED
|
@@ -175,12 +175,13 @@ export function Orgs({ children, front = false, forward, signup = false, }) {
|
|
|
175
175
|
if (!hydrated)
|
|
176
176
|
return front && Landing ? _jsx(Landing, {}) : _jsx(Veil, {});
|
|
177
177
|
// Past hydration the room keeps its place in the tree in every branch that
|
|
178
|
-
// draws it — the veil
|
|
178
|
+
// draws it — `Held`, the veil AFTER it — so a token refresh mid-session never
|
|
179
179
|
// remounts what the reader had open. The veil is unconditional now: it was
|
|
180
180
|
// hung only when a token was already in hand, which left a visitor arriving
|
|
181
|
-
// without one looking at the room while the SDK decided.
|
|
181
|
+
// without one looking at the room while the SDK decided. Under it the room is
|
|
182
|
+
// inert: it takes no key or press until the gate has decided.
|
|
182
183
|
if (isLoading)
|
|
183
|
-
return front && Landing && !hasSession() && !arriving ? _jsx(Landing, {}) : _jsxs(_Fragment, { children: [children, _jsx(Veil, {})] });
|
|
184
|
+
return front && Landing && !hasSession() && !arriving ? _jsx(Landing, {}) : _jsxs(_Fragment, { children: [_jsx(Held, { veiled: true, children: children }), _jsx(Veil, {})] });
|
|
184
185
|
if (arriving && !isAuthenticated)
|
|
185
186
|
return _jsx(SignIn, { to: LOGIN });
|
|
186
187
|
if (!isAuthenticated)
|
|
@@ -202,11 +203,11 @@ export function Orgs({ children, front = false, forward, signup = false, }) {
|
|
|
202
203
|
// exception rather than the fallback. The one exception to the plan is a
|
|
203
204
|
// personal organization on Free, off hanzo.team, and its kind is read first.
|
|
204
205
|
if (!tier && !wrong)
|
|
205
|
-
return _jsxs(_Fragment, { children: [children, _jsx(Veil, {})] });
|
|
206
|
+
return _jsxs(_Fragment, { children: [_jsx(Held, { veiled: true, children: children }), _jsx(Veil, {})] });
|
|
206
207
|
if (free && personal === null)
|
|
207
|
-
return _jsxs(_Fragment, { children: [children, _jsx(Veil, {})] });
|
|
208
|
+
return _jsxs(_Fragment, { children: [_jsx(Held, { veiled: true, children: children }), _jsx(Veil, {})] });
|
|
208
209
|
if (open)
|
|
209
|
-
return forward ? _jsx(Forward, { to: forward }) : _jsx(_Fragment, { children: children });
|
|
210
|
+
return forward ? _jsx(Forward, { to: forward }) : _jsx(_Fragment, { children: _jsx(Held, { veiled: false, children: children }) });
|
|
210
211
|
const swap = () => setPicked(false);
|
|
211
212
|
if (door) {
|
|
212
213
|
// A billing OUTAGE is ours; the plan is still the condition.
|
|
@@ -293,6 +294,16 @@ function Heading({ title, lede, lead }) {
|
|
|
293
294
|
function Veil() {
|
|
294
295
|
return _jsx(View, { "aria-hidden": true, position: "fixed", inset: 0, z: SHEET, bg: "var(--background)" });
|
|
295
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* The room, in the one place every branch that draws it puts it. Under the veil
|
|
299
|
+
* it is inert and hidden from assistive technology, so what the veil covers
|
|
300
|
+
* takes no focus, key or press: Dev's composer focuses itself as it mounts, and
|
|
301
|
+
* a team on Free could otherwise type and send before its ask was drawn. It
|
|
302
|
+
* lays out as its children do.
|
|
303
|
+
*/
|
|
304
|
+
function Held({ veiled, children }) {
|
|
305
|
+
return (_jsx(View, { render: _jsx("div", { "data-slot": "held", inert: veiled, "aria-hidden": veiled || undefined }), display: "contents", children: children }));
|
|
306
|
+
}
|
|
296
307
|
/** An open organization, sent on. */
|
|
297
308
|
function Forward({ to }) {
|
|
298
309
|
const { router } = useRooms();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/rooms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.51",
|
|
4
4
|
"description": "The workspace rooms — Home, Chat, Inbox, Contacts, Meet, Cal, Drive, Board, Work, Bots — and the frame that holds them, once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|