@unityevolv/ofiskit-ui-map 0.1.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/LICENSE +661 -0
- package/README.md +34 -0
- package/dist/Announcer.d.ts +21 -0
- package/dist/Announcer.d.ts.map +1 -0
- package/dist/Announcer.js +32 -0
- package/dist/Announcer.js.map +1 -0
- package/dist/CallAudio.d.ts +32 -0
- package/dist/CallAudio.d.ts.map +1 -0
- package/dist/CallAudio.js +72 -0
- package/dist/CallAudio.js.map +1 -0
- package/dist/CallControls.d.ts +60 -0
- package/dist/CallControls.d.ts.map +1 -0
- package/dist/CallControls.js +62 -0
- package/dist/CallControls.js.map +1 -0
- package/dist/CallTiles.d.ts +48 -0
- package/dist/CallTiles.d.ts.map +1 -0
- package/dist/CallTiles.js +134 -0
- package/dist/CallTiles.js.map +1 -0
- package/dist/DevicePanel.d.ts +32 -0
- package/dist/DevicePanel.d.ts.map +1 -0
- package/dist/DevicePanel.js +133 -0
- package/dist/DevicePanel.js.map +1 -0
- package/dist/Knocks.d.ts +46 -0
- package/dist/Knocks.d.ts.map +1 -0
- package/dist/Knocks.js +46 -0
- package/dist/Knocks.js.map +1 -0
- package/dist/OfficeMap.d.ts +69 -0
- package/dist/OfficeMap.d.ts.map +1 -0
- package/dist/OfficeMap.js +222 -0
- package/dist/OfficeMap.js.map +1 -0
- package/dist/Overflow.d.ts +41 -0
- package/dist/Overflow.d.ts.map +1 -0
- package/dist/Overflow.js +82 -0
- package/dist/Overflow.js.map +1 -0
- package/dist/PersonAvatar.d.ts +32 -0
- package/dist/PersonAvatar.d.ts.map +1 -0
- package/dist/PersonAvatar.js +80 -0
- package/dist/PersonAvatar.js.map +1 -0
- package/dist/Reactions.d.ts +47 -0
- package/dist/Reactions.d.ts.map +1 -0
- package/dist/Reactions.js +61 -0
- package/dist/Reactions.js.map +1 -0
- package/dist/RoomBar.d.ts +43 -0
- package/dist/RoomBar.d.ts.map +1 -0
- package/dist/RoomBar.js +120 -0
- package/dist/RoomBar.js.map +1 -0
- package/dist/Share.d.ts +74 -0
- package/dist/Share.d.ts.map +1 -0
- package/dist/Share.js +115 -0
- package/dist/Share.js.map +1 -0
- package/dist/StatusControl.d.ts +25 -0
- package/dist/StatusControl.d.ts.map +1 -0
- package/dist/StatusControl.js +77 -0
- package/dist/StatusControl.js.map +1 -0
- package/dist/controls.d.ts +32 -0
- package/dist/controls.d.ts.map +1 -0
- package/dist/controls.js +24 -0
- package/dist/controls.js.map +1 -0
- package/dist/hooks.d.ts +60 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +182 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +65 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +93 -0
- package/dist/layout.js.map +1 -0
- package/dist/placement.d.ts +70 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +83 -0
- package/dist/placement.js.map +1 -0
- package/dist/sounds.d.ts +30 -0
- package/dist/sounds.d.ts.map +1 -0
- package/dist/sounds.js +125 -0
- package/dist/sounds.js.map +1 -0
- package/dist/status.d.ts +47 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +65 -0
- package/dist/status.js.map +1 -0
- package/dist/theme.d.ts +27 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +57 -0
- package/dist/theme.js.map +1 -0
- package/dist/tileLayout.d.ts +27 -0
- package/dist/tileLayout.d.ts.map +1 -0
- package/dist/tileLayout.js +41 -0
- package/dist/tileLayout.js.map +1 -0
- package/dist/useCall.d.ts +107 -0
- package/dist/useCall.d.ts.map +1 -0
- package/dist/useCall.js +238 -0
- package/dist/useCall.js.map +1 -0
- package/dist/useShare.d.ts +54 -0
- package/dist/useShare.d.ts.map +1 -0
- package/dist/useShare.js +114 -0
- package/dist/useShare.js.map +1 -0
- package/dist/useSounds.d.ts +31 -0
- package/dist/useSounds.d.ts.map +1 -0
- package/dist/useSounds.js +56 -0
- package/dist/useSounds.js.map +1 -0
- package/package.json +25 -0
package/dist/Overflow.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Popover } from '@unityevolv/unitykit';
|
|
3
|
+
import { PersonAvatar } from './PersonAvatar.js';
|
|
4
|
+
/**
|
|
5
|
+
* The people a room has no cell for.
|
|
6
|
+
*
|
|
7
|
+
* User areas are display slots, not seats, so a room can hold more people than it
|
|
8
|
+
* has cells — and when it does, the last cell counts the rest. The counter is drawn
|
|
9
|
+
* **as an avatar** rather than as a badge: it takes a person's place in the room,
|
|
10
|
+
* it sits in a person's cell, and the thing it stands for is people. A small pill
|
|
11
|
+
* in a row of faces reads as a notification rather than as four more colleagues.
|
|
12
|
+
*
|
|
13
|
+
* Opening it shows those people the same way the room shows everybody else: as
|
|
14
|
+
* avatars, with their status and whatever they are doing in the call, three
|
|
15
|
+
* across and three down, scrolling for the rest. A list of names would be the one
|
|
16
|
+
* place in the office where people stopped looking like people.
|
|
17
|
+
*/
|
|
18
|
+
/** Three across and three down, then it scrolls. */
|
|
19
|
+
export const OVERFLOW_COLUMNS = 3;
|
|
20
|
+
export const OVERFLOW_ROWS = 3;
|
|
21
|
+
/** Each avatar in the grid, in pixels. Fixed, because the room's cells can be tiny. */
|
|
22
|
+
const CELL = 60;
|
|
23
|
+
/**
|
|
24
|
+
* One row: the face, the name under it, and room above it for a raised hand, which
|
|
25
|
+
* sits outside the circle on purpose and would otherwise be clipped by the row
|
|
26
|
+
* above.
|
|
27
|
+
*/
|
|
28
|
+
const ROW = 72;
|
|
29
|
+
const GAP = 4;
|
|
30
|
+
/**
|
|
31
|
+
* How tall the list may be.
|
|
32
|
+
*
|
|
33
|
+
* Three rows, and the rest scroll: a room of forty people is a panel of nine faces
|
|
34
|
+
* and a scrollbar, not a panel taller than the map. Fewer when the screen has less
|
|
35
|
+
* room than that on either side of the counter — Radix measures what is available,
|
|
36
|
+
* and a short phone may have less than three rows' worth above or below. The
|
|
37
|
+
* subtraction is the panel's own padding.
|
|
38
|
+
*/
|
|
39
|
+
export const OVERFLOW_MAX_HEIGHT = `min(${OVERFLOW_ROWS * ROW + (OVERFLOW_ROWS - 1) * GAP}px, calc(var(--radix-popover-content-available-height, 100vh) - 3.5rem))`;
|
|
40
|
+
/**
|
|
41
|
+
* Give the popover's dialog the list's name.
|
|
42
|
+
*
|
|
43
|
+
* The kit's Popover renders Radix's content as `role="dialog"` and has no prop to
|
|
44
|
+
* name it, so without this every screen reader announces an unnamed dialog before
|
|
45
|
+
* it reaches the list. Set from inside, once the list is mounted, because the
|
|
46
|
+
* dialog is the list's own ancestor and nothing outside the popover can reach it.
|
|
47
|
+
* Worth moving into the kit, where every popover would get it.
|
|
48
|
+
*/
|
|
49
|
+
const nameTheDialog = (name) => (node) => {
|
|
50
|
+
node?.closest('[role="dialog"]')?.setAttribute('aria-label', name);
|
|
51
|
+
};
|
|
52
|
+
export function OverflowAvatar({ roomName, tokens, size, reducedMotion = false, reactions, }) {
|
|
53
|
+
const count = tokens.length;
|
|
54
|
+
// The same proportions as a person's avatar, so the counter lines up with the
|
|
55
|
+
// row it is in rather than looking like something laid on top of it.
|
|
56
|
+
const face = Math.round(size * 0.62);
|
|
57
|
+
const label = `+${count}`;
|
|
58
|
+
return (_jsx(Popover, { width: "auto", trigger: _jsxs("button", { type: "button", "aria-label": `${count} more in ${roomName}. Activate to see them.`, "data-testid": "overflow-avatar", className: "flex cursor-pointer select-none flex-col items-center gap-1 rounded-lg focus-visible:outline-2 focus-visible:outline-primary", style: { width: size }, children: [_jsx("span", { "aria-hidden": "true", className: "grid place-items-center rounded-full bg-neutral font-semibold text-neutral-content ring-1 ring-base-300",
|
|
59
|
+
// Three characters from +10 upwards, so the digits shrink rather than
|
|
60
|
+
// spill out of the circle.
|
|
61
|
+
style: {
|
|
62
|
+
width: face,
|
|
63
|
+
height: face,
|
|
64
|
+
fontSize: Math.max(10, face * (label.length > 2 ? 0.32 : 0.38)),
|
|
65
|
+
}, children: label }), _jsx("span", { "aria-hidden": "true", className: "max-w-full truncate rounded bg-base-100/85 px-1 text-center leading-tight text-base-content backdrop-blur-[2px]", style: { fontSize: Math.max(9, size * 0.17) }, children: "more" })] }), children: _jsx("ul", { "aria-label": `${count} more in ${roomName}`, "data-testid": "overflow-grid",
|
|
66
|
+
/*
|
|
67
|
+
Focusable, so the keyboard can scroll it.
|
|
68
|
+
|
|
69
|
+
Nothing inside is focusable — an avatar here is a picture of a person, not
|
|
70
|
+
a control — so without this the people after the ninth could be seen with
|
|
71
|
+
a mouse wheel and reached by nobody else. Being the first thing that takes
|
|
72
|
+
focus also means the popover opens onto a named list rather than onto its
|
|
73
|
+
own frame.
|
|
74
|
+
*/
|
|
75
|
+
tabIndex: 0, ref: nameTheDialog(`${count} more in ${roomName}`), className: "grid overflow-y-auto overscroll-contain rounded focus-visible:outline-2 focus-visible:outline-primary", style: {
|
|
76
|
+
gridTemplateColumns: `repeat(${OVERFLOW_COLUMNS}, ${CELL}px)`,
|
|
77
|
+
gridAutoRows: `${ROW}px`,
|
|
78
|
+
gap: GAP,
|
|
79
|
+
maxHeight: OVERFLOW_MAX_HEIGHT,
|
|
80
|
+
}, children: tokens.map((token) => (_jsx("li", { className: "flex justify-center pt-2.5", children: _jsx(PersonAvatar, { person: token.person, size: CELL, ...(token.deviceId ? { deviceId: token.deviceId } : {}), linked: token.linked, reducedMotion: reducedMotion, reactions: reactions?.get(token.person.userId) ?? [] }) }, token.key))) }) }));
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=Overflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Overflow.js","sourceRoot":"","sources":["../src/Overflow.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PublicPresence } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
import type { LiveReaction } from './useCall.js';
|
|
3
|
+
/**
|
|
4
|
+
* One person, standing in a room.
|
|
5
|
+
*
|
|
6
|
+
* Drawn as a kit surface rather than straight onto the background image: the
|
|
7
|
+
* image is whatever an author generated, and nothing legible can be guaranteed on
|
|
8
|
+
* top of it. Everything here has its own background and its own contrast in both
|
|
9
|
+
* themes.
|
|
10
|
+
*/
|
|
11
|
+
export interface PersonAvatarProps {
|
|
12
|
+
person: PublicPresence;
|
|
13
|
+
/** The avatar cell's width in pixels, from the template's avatar size. */
|
|
14
|
+
size: number;
|
|
15
|
+
/**
|
|
16
|
+
* Which of this person's devices this avatar is for.
|
|
17
|
+
*
|
|
18
|
+
* Somebody here on two devices is drawn twice, one avatar per device, because
|
|
19
|
+
* each is a real screen with its own camera and microphone. Both are the same
|
|
20
|
+
* person, and the pair says so.
|
|
21
|
+
*/
|
|
22
|
+
deviceId?: string;
|
|
23
|
+
/** Linked to another avatar for the same person, so the pair reads as one. */
|
|
24
|
+
linked?: boolean;
|
|
25
|
+
/** Drops the pulse on the speaking ring. The ring itself stays. */
|
|
26
|
+
reducedMotion?: boolean;
|
|
27
|
+
/** What is in the air over this person right now. From `useReactions`. */
|
|
28
|
+
reactions?: readonly LiveReaction[];
|
|
29
|
+
onClick?(): void;
|
|
30
|
+
}
|
|
31
|
+
export declare function PersonAvatar({ person, size, deviceId, linked, reducedMotion, reactions, onClick, }: PersonAvatarProps): import("react").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=PersonAvatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PersonAvatar.d.ts","sourceRoot":"","sources":["../src/PersonAvatar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AAKzE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;GAOG;AAEH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAA;IACtB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,SAAS,YAAY,EAAE,CAAA;IACnC,OAAO,CAAC,IAAI,IAAI,CAAA;CACjB;AAmCD,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,MAAc,EACd,aAAqB,EACrB,SAAS,EACT,OAAO,GACR,EAAE,iBAAiB,+BAoMnB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Icon, Tooltip } from '@unityevolv/unitykit';
|
|
3
|
+
import { handRaised, isMuted, isPhoneOnly, isSharing, isSpeaking } from '@unityevolv/ofiskit-realtime-client';
|
|
4
|
+
import { ReactionFloat } from './Reactions.js';
|
|
5
|
+
import { StatusDot, describeStatus } from './status.js';
|
|
6
|
+
/**
|
|
7
|
+
* A phone, drawn here rather than imported.
|
|
8
|
+
*
|
|
9
|
+
* The kit's icon set has no phone in it, and `ofiskit/no-lucide-direct` exists to
|
|
10
|
+
* stop anybody reaching around the kit for one — an icon from outside is a
|
|
11
|
+
* different weight from every other icon in the product. Drawn inline instead, in
|
|
12
|
+
* `currentColor`, which is the same thing `status.tsx` does for the status shapes
|
|
13
|
+
* and for the same reason. It is decoration: the avatar's label already says "on a
|
|
14
|
+
* phone".
|
|
15
|
+
*/
|
|
16
|
+
function PhoneGlyph({ size }) {
|
|
17
|
+
return (_jsxs("svg", { viewBox: "0 0 12 12", width: size, height: size, "aria-hidden": "true", focusable: "false", children: [_jsx("rect", { x: "3.25", y: "1", width: "5.5", height: "10", rx: "1.25", fill: "none", stroke: "currentColor", strokeWidth: "1.25" }), _jsx("rect", { x: "5", y: "9", width: "2", height: "0.9", rx: "0.45", fill: "currentColor" })] }));
|
|
18
|
+
}
|
|
19
|
+
function initials(name) {
|
|
20
|
+
const parts = name.trim().split(/\s+/).slice(0, 2);
|
|
21
|
+
return parts.map((part) => part[0]?.toUpperCase() ?? '').join('') || '?';
|
|
22
|
+
}
|
|
23
|
+
export function PersonAvatar({ person, size, deviceId, linked = false, reducedMotion = false, reactions, onClick, }) {
|
|
24
|
+
const device = deviceId ? person.devices.find((one) => one.deviceId === deviceId) : undefined;
|
|
25
|
+
const reconnecting = person.status === 'reconnecting';
|
|
26
|
+
/*
|
|
27
|
+
* What the call looks like from the map.
|
|
28
|
+
*
|
|
29
|
+
* Per device where this avatar *is* a device, and across the person otherwise —
|
|
30
|
+
* somebody drawn once while in the call from a laptop and a phone is speaking if
|
|
31
|
+
* either of them is, and muted only if both are.
|
|
32
|
+
*/
|
|
33
|
+
const speaking = device ? device.inCall && device.speaking : isSpeaking(person);
|
|
34
|
+
const microphoneOff = device ? device.inCall && device.muted : isMuted(person);
|
|
35
|
+
const sharing = device ? device.sharing : isSharing(person);
|
|
36
|
+
const hand = device ? device.inCall && device.handRaisedAt !== null : handRaised(person);
|
|
37
|
+
const face = Math.round(size * 0.62);
|
|
38
|
+
const description = describeStatus(person.status, person.custom);
|
|
39
|
+
/**
|
|
40
|
+
* One label carrying everything the picture says.
|
|
41
|
+
*
|
|
42
|
+
* Drawing is not telling: a badge in the corner of an avatar is invisible to a
|
|
43
|
+
* screen reader unless it is in here, and status is the whole reason somebody
|
|
44
|
+
* decides whether to walk into a room.
|
|
45
|
+
*/
|
|
46
|
+
const label = [
|
|
47
|
+
person.displayName,
|
|
48
|
+
description,
|
|
49
|
+
linked ? `also here on another device${device ? ` (this one is ${device.kind})` : ''}` : '',
|
|
50
|
+
isPhoneOnly(person) ? 'on a phone' : '',
|
|
51
|
+
// Every badge drawn in a corner of this avatar, said. A ring on a picture is
|
|
52
|
+
// invisible to a screen reader, and "who is talking" is most of what somebody
|
|
53
|
+
// wants from a room they cannot see.
|
|
54
|
+
hand ? 'hand raised' : '',
|
|
55
|
+
sharing ? 'sharing their screen' : '',
|
|
56
|
+
microphoneOff ? 'microphone off' : '',
|
|
57
|
+
speaking ? 'speaking' : '',
|
|
58
|
+
]
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(', ');
|
|
61
|
+
const body = (_jsxs("span", { className: "flex select-none flex-col items-center gap-1", style: { width: size }, "data-testid": "person-avatar", children: [_jsxs("span", { className: "relative inline-flex", style: { width: face, height: face }, children: [_jsx(ReactionFloat, { reactions: reactions ?? [], size: Math.max(16, Math.round(face * 0.5)) }), linked && (_jsx("span", { "aria-hidden": "true", className: "absolute -inset-1 rounded-full ring-2 ring-primary/50" })), speaking && (_jsx("span", { "aria-hidden": "true", className: [
|
|
62
|
+
'absolute -inset-1 rounded-full ring-2 ring-primary',
|
|
63
|
+
reducedMotion ? '' : 'animate-pulse',
|
|
64
|
+
].join(' ') })), person.photoUrl ? (_jsx("img", { src: person.photoUrl, alt: "", className: [
|
|
65
|
+
'rounded-full bg-base-200 object-cover ring-1 ring-base-300',
|
|
66
|
+
reconnecting ? 'opacity-50' : '',
|
|
67
|
+
].join(' '), style: { width: face, height: face } })) : (_jsx("span", { "aria-hidden": "true", className: [
|
|
68
|
+
'grid place-items-center rounded-full bg-base-300 font-semibold text-base-content ring-1 ring-base-300',
|
|
69
|
+
reconnecting ? 'opacity-50' : '',
|
|
70
|
+
].join(' '), style: { width: face, height: face, fontSize: Math.max(10, face * 0.38) }, children: initials(person.displayName) })), hand && (_jsx("span", { "aria-hidden": "true", className: "absolute -top-2.5 left-1/2 -translate-x-1/2 rounded-full bg-base-100 px-0.5 leading-none text-primary", children: _jsx(Icon, { name: "raise-hand", size: "xs" }) })), sharing && (_jsx("span", { "aria-hidden": "true", className: "absolute -right-1 -top-1 rounded-full bg-base-100 p-0.5 leading-none text-primary", children: _jsx(Icon, { name: "share", size: "xs" }) })), microphoneOff && (_jsx("span", { "aria-hidden": "true", className: "absolute -left-1 -top-1 rounded-full bg-base-100 p-0.5 leading-none text-base-content/70", children: _jsx(Icon, { name: "mic-off", size: "xs" }) })), _jsx("span", { className: "absolute -bottom-0.5 -right-0.5 rounded-full bg-base-100 p-px leading-none", children: _jsx(StatusDot, { status: person.status, size: Math.max(9, face * 0.3), labelled: false }) }), isPhoneOnly(person) && (_jsx("span", { className: "absolute -bottom-1 -left-1 rounded-full bg-base-100 p-0.5 leading-none text-base-content/70", children: _jsx(PhoneGlyph, { size: Math.max(8, face * 0.26) }) }))] }), _jsx("span", { className: [
|
|
71
|
+
'max-w-full truncate rounded px-1 text-center leading-tight',
|
|
72
|
+
'bg-base-100/85 text-base-content backdrop-blur-[2px]',
|
|
73
|
+
linked ? 'ring-1 ring-primary/60' : '',
|
|
74
|
+
].join(' '), style: { fontSize: Math.max(9, size * 0.17) }, children: person.displayName })] }));
|
|
75
|
+
const content = onClick ? (_jsx("button", { type: "button", onClick: onClick, className: "cursor-pointer", "aria-label": label, children: body })) : (_jsx("span", { role: "img", "aria-label": label, children: body }));
|
|
76
|
+
// The custom status is shown on hover on the web, so the map is not covered in
|
|
77
|
+
// text while still carrying it for anybody who wants it.
|
|
78
|
+
return person.custom?.text ? _jsx(Tooltip, { content: description, children: content }) : content;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=PersonAvatar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PersonAvatar.js","sourceRoot":"","sources":["../src/PersonAvatar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEpD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAA;AAE7G,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAiCvD;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAC,EAAE,IAAI,EAAoB;IAC5C,OAAO,CACL,eAAK,OAAO,EAAC,WAAW,EAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,iBAAc,MAAM,EAAC,SAAS,EAAC,OAAO,aACtF,eACE,CAAC,EAAC,MAAM,EACR,CAAC,EAAC,GAAG,EACL,KAAK,EAAC,KAAK,EACX,MAAM,EAAC,IAAI,EACX,EAAE,EAAC,MAAM,EACT,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,MAAM,GAClB,EACF,eAAM,CAAC,EAAC,GAAG,EAAC,CAAC,EAAC,GAAG,EAAC,KAAK,EAAC,GAAG,EAAC,MAAM,EAAC,KAAK,EAAC,EAAE,EAAC,MAAM,EAAC,IAAI,EAAC,cAAc,GAAG,IACrE,CACP,CAAA;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IAClD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAA;AAC1E,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAC3B,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,MAAM,GAAG,KAAK,EACd,aAAa,GAAG,KAAK,EACrB,SAAS,EACT,OAAO,GACW;IAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7F,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,KAAK,cAAc,CAAA;IAErD;;;;;;OAMG;IACH,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IAC/E,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9E,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IAExF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAA;IACpC,MAAM,WAAW,GAAG,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IAEhE;;;;;;OAMG;IACH,MAAM,KAAK,GAAG;QACZ,MAAM,CAAC,WAAW;QAClB,WAAW;QACX,MAAM,CAAC,CAAC,CAAC,8BAA8B,MAAM,CAAC,CAAC,CAAC,iBAAiB,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE;QAC3F,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;QACvC,6EAA6E;QAC7E,8EAA8E;QAC9E,qCAAqC;QACrC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE;QACzB,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE;QACrC,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE;QACrC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE;KAC3B;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,IAAI,GAAG,CACX,gBACE,SAAS,EAAC,8CAA8C,EACxD,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,iBACV,eAAe,aAE3B,gBAAM,SAAS,EAAC,sBAAsB,EAAC,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAEzE,KAAC,aAAa,IAAC,SAAS,EAAE,SAAS,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,GAAI,EAOxF,MAAM,IAAI,CACT,8BACc,MAAM,EAClB,SAAS,EAAC,uDAAuD,GACjE,CACH,EASA,QAAQ,IAAI,CACX,8BACc,MAAM,EAClB,SAAS,EAAE;4BACT,oDAAoD;4BACpD,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe;yBACrC,CAAC,IAAI,CAAC,GAAG,CAAC,GACX,CACH,EAEA,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjB,cACE,GAAG,EAAE,MAAM,CAAC,QAAQ,EACpB,GAAG,EAAC,EAAE,EACN,SAAS,EAAE;4BACT,4DAA4D;4BAC5D,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;yBACjC,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GACpC,CACH,CAAC,CAAC,CAAC,CACF,8BACc,MAAM,EAClB,SAAS,EAAE;4BACT,uGAAuG;4BACvG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE;yBACjC,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,GAAG,IAAI,CAAC,EAAE,YAExE,QAAQ,CAAC,MAAM,CAAC,WAAW,CAAC,GACxB,CACR,EAUA,IAAI,IAAI,CACP,8BACc,MAAM,EAClB,SAAS,EAAC,uGAAuG,YAEjH,KAAC,IAAI,IAAC,IAAI,EAAC,YAAY,EAAC,IAAI,EAAC,IAAI,GAAG,GAC/B,CACR,EAQA,OAAO,IAAI,CACV,8BACc,MAAM,EAClB,SAAS,EAAC,mFAAmF,YAE7F,KAAC,IAAI,IAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,IAAI,GAAG,GAC1B,CACR,EAUA,aAAa,IAAI,CAChB,8BACc,MAAM,EAClB,SAAS,EAAC,0FAA0F,YAEpG,KAAC,IAAI,IAAC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,GAAG,GAC5B,CACR,EAGD,eAAM,SAAS,EAAC,4EAA4E,YAC1F,KAAC,SAAS,IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,GAAG,CAAC,EAAE,QAAQ,EAAE,KAAK,GAAI,GAC/E,EAON,WAAW,CAAC,MAAM,CAAC,IAAI,CACtB,eAAM,SAAS,EAAC,6FAA6F,YAC3G,KAAC,UAAU,IAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,GAAI,GACzC,CACR,IACI,EAEP,eACE,SAAS,EAAE;oBACT,4DAA4D;oBAC5D,sDAAsD;oBACtD,MAAM,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;iBACvC,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,EAAE,YAE5C,MAAM,CAAC,WAAW,GACd,IACF,CACR,CAAA;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CACxB,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAC,gBAAgB,gBAAa,KAAK,YACjF,IAAI,GACE,CACV,CAAC,CAAC,CAAC,CACF,eAAM,IAAI,EAAC,KAAK,gBAAa,KAAK,YAC/B,IAAI,GACA,CACR,CAAA;IAED,+EAA+E;IAC/E,yDAAyD;IACzD,OAAO,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,KAAC,OAAO,IAAC,OAAO,EAAE,WAAW,YAAG,OAAO,GAAW,CAAC,CAAC,CAAC,OAAO,CAAA;AAC3F,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Reaction } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
import type { LiveReaction } from './useCall.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reacting without interrupting, and seeing that somebody did.
|
|
5
|
+
*
|
|
6
|
+
* Two pieces that belong together: the picker somebody sends from, and the float
|
|
7
|
+
* that appears over whoever sent it. Both are here so the set of reactions is read
|
|
8
|
+
* from one place and drawn the same way over a tile and over an avatar.
|
|
9
|
+
*
|
|
10
|
+
* **Nothing is stored.** A reaction floats for a few seconds and then it has
|
|
11
|
+
* stopped existing — not archived, not in the office state, not on the server.
|
|
12
|
+
* Somebody who was not looking missed it, which is what happens with a nod.
|
|
13
|
+
*/
|
|
14
|
+
export interface ReactionFloatProps {
|
|
15
|
+
/** What is in the air over this person right now. Empty draws nothing at all. */
|
|
16
|
+
reactions: readonly LiveReaction[];
|
|
17
|
+
/** Roughly how big to draw them, in pixels. A tile wants more than an avatar. */
|
|
18
|
+
size?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The reactions in the air over one person.
|
|
22
|
+
*
|
|
23
|
+
* Absolutely positioned and `pointer-events-none`, so it can never take a click
|
|
24
|
+
* meant for the tile or the avatar underneath it — a reaction that swallows the
|
|
25
|
+
* mute button is worse than no reaction.
|
|
26
|
+
*
|
|
27
|
+
* Announced politely rather than drawn only. It is deliberately **not** assertive:
|
|
28
|
+
* applause must not interrupt a screen reader mid-sentence, which is the whole
|
|
29
|
+
* point of reacting this way rather than saying something.
|
|
30
|
+
*/
|
|
31
|
+
export declare function ReactionFloat({ reactions, size }: ReactionFloatProps): import("react").JSX.Element | null;
|
|
32
|
+
export interface ReactionPickerProps {
|
|
33
|
+
onReact(reaction: Reaction): void;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The six, in a row.
|
|
38
|
+
*
|
|
39
|
+
* A row rather than a menu behind a menu: this is pressed in the middle of
|
|
40
|
+
* somebody else's sentence, and anything that takes two decisions will not be
|
|
41
|
+
* used. Every one is a real button with a real name, because an emoji on its own
|
|
42
|
+
* is not a label — "thumbs up" is.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ReactionPicker({ onReact, disabled }: ReactionPickerProps): import("react").JSX.Element;
|
|
45
|
+
/** The words a live region says when somebody reacts. Used by the host's announcer. */
|
|
46
|
+
export declare function describeReaction(displayName: string, reaction: string): string;
|
|
47
|
+
//# sourceMappingURL=Reactions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Reactions.d.ts","sourceRoot":"","sources":["../src/Reactions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AAInE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,SAAS,EAAE,SAAS,YAAY,EAAE,CAAA;IAClC,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,IAAS,EAAE,EAAE,kBAAkB,sCA4BzE;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,QAAgB,EAAE,EAAE,mBAAmB,+BA0BhF;AAmBD,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG9E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { REACTIONS } from '@unityevolv/ofiskit-realtime-client';
|
|
3
|
+
import { useReducedMotion } from './hooks.js';
|
|
4
|
+
/**
|
|
5
|
+
* The reactions in the air over one person.
|
|
6
|
+
*
|
|
7
|
+
* Absolutely positioned and `pointer-events-none`, so it can never take a click
|
|
8
|
+
* meant for the tile or the avatar underneath it — a reaction that swallows the
|
|
9
|
+
* mute button is worse than no reaction.
|
|
10
|
+
*
|
|
11
|
+
* Announced politely rather than drawn only. It is deliberately **not** assertive:
|
|
12
|
+
* applause must not interrupt a screen reader mid-sentence, which is the whole
|
|
13
|
+
* point of reacting this way rather than saying something.
|
|
14
|
+
*/
|
|
15
|
+
export function ReactionFloat({ reactions, size = 22 }) {
|
|
16
|
+
const reducedMotion = useReducedMotion();
|
|
17
|
+
if (reactions.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
return (_jsx("span", { className: "pointer-events-none absolute inset-x-0 bottom-1/3 z-20 flex items-end justify-center gap-0.5", "data-testid": "reaction-float", children: reactions.map((one) => (_jsx("span", {
|
|
20
|
+
// A bob rather than a static badge, because a reaction that appears with
|
|
21
|
+
// no movement reads as a badge somebody earned rather than as a thing
|
|
22
|
+
// that just happened. Still, and still visible, under reduced motion.
|
|
23
|
+
className: ['leading-none drop-shadow', reducedMotion ? '' : 'animate-bounce'].join(' '), style: { fontSize: size }, children: _jsx("span", { "aria-hidden": "true", children: one.reaction }) }, one.id))) }));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The six, in a row.
|
|
27
|
+
*
|
|
28
|
+
* A row rather than a menu behind a menu: this is pressed in the middle of
|
|
29
|
+
* somebody else's sentence, and anything that takes two decisions will not be
|
|
30
|
+
* used. Every one is a real button with a real name, because an emoji on its own
|
|
31
|
+
* is not a label — "thumbs up" is.
|
|
32
|
+
*/
|
|
33
|
+
export function ReactionPicker({ onReact, disabled = false }) {
|
|
34
|
+
return (_jsx("div", { role: "group", "aria-label": "React", "data-testid": "reaction-picker", className: "flex items-center gap-0.5", children: REACTIONS.map((reaction) => (_jsx("button", { type: "button", disabled: disabled, onClick: () => onReact(reaction), "aria-label": `React with ${NAMES[reaction]}`, className: [
|
|
35
|
+
'inline-flex h-8 w-8 items-center justify-center rounded-lg text-lg leading-none',
|
|
36
|
+
'focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary',
|
|
37
|
+
disabled ? 'cursor-not-allowed opacity-40' : 'hover:bg-base-200',
|
|
38
|
+
].join(' '), children: _jsx("span", { "aria-hidden": "true", children: reaction }) }, reaction))) }));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* What each one is called.
|
|
42
|
+
*
|
|
43
|
+
* Here rather than in the protocol, because a name is for a person to read and the
|
|
44
|
+
* protocol carries what both sides must agree on. The keys are exhaustive against
|
|
45
|
+
* the set, so adding a reaction without naming it is a compile error rather than a
|
|
46
|
+
* button announced as "react with".
|
|
47
|
+
*/
|
|
48
|
+
const NAMES = {
|
|
49
|
+
'👍': 'thumbs up',
|
|
50
|
+
'👏': 'applause',
|
|
51
|
+
'🎉': 'celebration',
|
|
52
|
+
'😂': 'laughing',
|
|
53
|
+
'❤️': 'love',
|
|
54
|
+
'😮': 'surprise',
|
|
55
|
+
};
|
|
56
|
+
/** The words a live region says when somebody reacts. Used by the host's announcer. */
|
|
57
|
+
export function describeReaction(displayName, reaction) {
|
|
58
|
+
const name = NAMES[reaction];
|
|
59
|
+
return name ? `${displayName} reacted: ${name}.` : `${displayName} reacted.`;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=Reactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Reactions.js","sourceRoot":"","sources":["../src/Reactions.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,qCAAqC,CAAA;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAsB7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAsB;IACxE,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;IACxC,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvC,OAAO,CACL,eACE,SAAS,EAAC,8FAA8F,iBAC5F,gBAAgB,YAE3B,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACtB;YAEE,yEAAyE;YACzE,sEAAsE;YACtE,sEAAsE;YACtE,SAAS,EAAE,CAAC,0BAA0B,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EACxF,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,YAOzB,8BAAkB,MAAM,YAAE,GAAG,CAAC,QAAQ,GAAQ,IAZzC,GAAG,CAAC,EAAE,CAaN,CACR,CAAC,GACG,CACR,CAAA;AACH,CAAC;AAOD;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,OAAO,EAAE,QAAQ,GAAG,KAAK,EAAuB;IAC/E,OAAO,CACL,cACE,IAAI,EAAC,OAAO,gBACD,OAAO,iBACN,iBAAiB,EAC7B,SAAS,EAAC,2BAA2B,YAEpC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAC3B,iBAEE,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,gBACpB,cAAc,KAAK,CAAC,QAAQ,CAAC,EAAE,EAC3C,SAAS,EAAE;gBACT,iFAAiF;gBACjF,sFAAsF;gBACtF,QAAQ,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,mBAAmB;aACjE,CAAC,IAAI,CAAC,GAAG,CAAC,YAEX,8BAAkB,MAAM,YAAE,QAAQ,GAAQ,IAXrC,QAAQ,CAYN,CACV,CAAC,GACE,CACP,CAAA;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,KAAK,GAA6B;IACtC,IAAI,EAAE,WAAW;IACjB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,aAAa;IACnB,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,UAAU;CACjB,CAAA;AAED,uFAAuF;AACvF,MAAM,UAAU,gBAAgB,CAAC,WAAmB,EAAE,QAAgB;IACpE,MAAM,IAAI,GAAG,KAAK,CAAC,QAAoB,CAAC,CAAA;IACxC,OAAO,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,aAAa,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,WAAW,WAAW,CAAA;AAC9E,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RoomCall } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
import type { Room } from '@unityevolv/ofiskit-template';
|
|
3
|
+
/**
|
|
4
|
+
* The bar on a room: its name, what is happening in it, and what you can do.
|
|
5
|
+
*
|
|
6
|
+
* Two rows. The action row carries the name, the occupancy, the lock state and
|
|
7
|
+
* the controls. The message row underneath explains why a control is disabled,
|
|
8
|
+
* and is collapsed entirely when there is nothing to say, so an ordinary open room
|
|
9
|
+
* does not carry a blank line.
|
|
10
|
+
*
|
|
11
|
+
* Controls that cannot be used are **disabled and visible, never hidden**. A
|
|
12
|
+
* person needs to know that joining is a thing that exists and why they cannot do
|
|
13
|
+
* it right now; a control that vanishes teaches them nothing. The server refuses
|
|
14
|
+
* the same action independently, so the disabled state is a convenience and never
|
|
15
|
+
* the control.
|
|
16
|
+
*/
|
|
17
|
+
export interface RoomBarProps {
|
|
18
|
+
room: Room;
|
|
19
|
+
occupancy: number;
|
|
20
|
+
/** Null when the office does not cap rooms, which the free office never does. */
|
|
21
|
+
capacity: number | null;
|
|
22
|
+
locked: boolean;
|
|
23
|
+
inside: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The call happening in this room, if there is one.
|
|
26
|
+
*
|
|
27
|
+
* Null in reception and the break room always, because neither hosts a call —
|
|
28
|
+
* and null in an ordinary room where nobody has started one yet. A room with a
|
|
29
|
+
* call in it is the single most useful thing on this bar: it is the difference
|
|
30
|
+
* between walking in on a conversation and joining one.
|
|
31
|
+
*/
|
|
32
|
+
call?: RoomCall | null;
|
|
33
|
+
/** The identity adapter's answer, when the host has one. Never invented here. */
|
|
34
|
+
forbiddenReason?: string | null;
|
|
35
|
+
/** How wide the room is on screen, which decides how much the bar can show. */
|
|
36
|
+
width: number;
|
|
37
|
+
onJoin(): void;
|
|
38
|
+
onKnock(): void;
|
|
39
|
+
onLock(): void;
|
|
40
|
+
onUnlock(): void;
|
|
41
|
+
}
|
|
42
|
+
export declare function RoomBar(props: RoomBarProps): import("react").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=RoomBar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoomBar.d.ts","sourceRoot":"","sources":["../src/RoomBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAA;AACnE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,8BAA8B,CAAA;AAGxD;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IACtB,iFAAiF;IACjF,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,IAAI,IAAI,CAAA;IACd,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;IACd,QAAQ,IAAI,IAAI,CAAA;CACjB;AAsCD,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,+BA8N1C"}
|
package/dist/RoomBar.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Icon } from '@unityevolv/unitykit';
|
|
3
|
+
import { hostsCalls } from '@unityevolv/ofiskit-template';
|
|
4
|
+
/** Below this, the bar shows icons only and the name is truncated. */
|
|
5
|
+
const COMPACT_BELOW = 190;
|
|
6
|
+
/**
|
|
7
|
+
* Below this there is room for the name and the action and nothing else.
|
|
8
|
+
*
|
|
9
|
+
* A room this narrow is a room on a phone's map, where a bar is about 65 to 90
|
|
10
|
+
* pixels wide. The name at its two-character minimum, the gaps and a worded Join
|
|
11
|
+
* come to about 62, so a call badge beside them — even as a bare icon — would push
|
|
12
|
+
* Join off the bar's clipped edge.
|
|
13
|
+
*/
|
|
14
|
+
const TINY_BELOW = 110;
|
|
15
|
+
export function RoomBar(props) {
|
|
16
|
+
const { room, occupancy, capacity, locked, inside, forbiddenReason, width } = props;
|
|
17
|
+
const compact = width < COMPACT_BELOW;
|
|
18
|
+
const tiny = width < TINY_BELOW;
|
|
19
|
+
const full = capacity !== null && occupancy >= capacity;
|
|
20
|
+
const lockable = hostsCalls(room.type);
|
|
21
|
+
/*
|
|
22
|
+
* The call, and whether there is a seat left in it.
|
|
23
|
+
*
|
|
24
|
+
* Two different fullnesses live on this bar and they are not the same thing: the
|
|
25
|
+
* room can have space for ten more people while the call in it has none, because
|
|
26
|
+
* the cap on the call is the provider's and the cap on the room is the office's.
|
|
27
|
+
* Saying "full" without saying which would send somebody away from a room they
|
|
28
|
+
* could have walked into.
|
|
29
|
+
*/
|
|
30
|
+
const call = hostsCalls(room.type) ? (props.call ?? null) : null;
|
|
31
|
+
const callFull = call !== null && call.participants.length >= call.limit;
|
|
32
|
+
const actions = [];
|
|
33
|
+
if (!inside) {
|
|
34
|
+
if (locked) {
|
|
35
|
+
actions.push({
|
|
36
|
+
key: 'knock',
|
|
37
|
+
label: 'Knock',
|
|
38
|
+
onClick: props.onKnock,
|
|
39
|
+
disabled: Boolean(forbiddenReason),
|
|
40
|
+
...(forbiddenReason ? { reason: forbiddenReason } : {}),
|
|
41
|
+
variant: 'secondary',
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// The adapter's reason wins over the room being full: "you are a guest" is
|
|
46
|
+
// more useful than "it is full", because one of them will still be true in
|
|
47
|
+
// ten minutes.
|
|
48
|
+
const reason = forbiddenReason ?? (full ? `${room.name} is full.` : undefined);
|
|
49
|
+
actions.push({
|
|
50
|
+
key: 'join',
|
|
51
|
+
label: 'Join',
|
|
52
|
+
onClick: props.onJoin,
|
|
53
|
+
disabled: Boolean(reason),
|
|
54
|
+
...(reason ? { reason } : {}),
|
|
55
|
+
variant: 'primary',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (lockable) {
|
|
60
|
+
actions.push(locked
|
|
61
|
+
? {
|
|
62
|
+
key: 'unlock',
|
|
63
|
+
label: 'Unlock',
|
|
64
|
+
icon: 'unlock',
|
|
65
|
+
onClick: props.onUnlock,
|
|
66
|
+
disabled: false,
|
|
67
|
+
variant: 'secondary',
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
key: 'lock',
|
|
71
|
+
label: 'Lock',
|
|
72
|
+
icon: 'lock',
|
|
73
|
+
onClick: props.onLock,
|
|
74
|
+
disabled: false,
|
|
75
|
+
variant: 'secondary',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// One message at a time. Two stacked reasons is a paragraph on a room bar, and
|
|
79
|
+
// the first one is the one stopping you. A full call comes second, because it
|
|
80
|
+
// stops you doing less: you can still go in and listen.
|
|
81
|
+
const message = actions.find((action) => action.disabled)?.reason ??
|
|
82
|
+
(callFull ? `The call in ${room.name} is full. You can still go in.` : null);
|
|
83
|
+
const typeIcon = room.type === 'reception' ? 'reception' : room.type === 'break' ? 'break-room' : 'office';
|
|
84
|
+
return (_jsxs("div", {
|
|
85
|
+
// Translucent enough for the room's picture to show through, which is what
|
|
86
|
+
// ties the bar to the room it sits on. The blur is what keeps it readable over
|
|
87
|
+
// any picture: the surface still carries the contrast, never the image.
|
|
88
|
+
className: "pointer-events-auto flex w-full flex-col overflow-hidden rounded-md bg-base-100/50 text-base-content shadow backdrop-blur-sm", "data-testid": `room-bar-${room.id}`, children: [_jsxs("div", { className: "flex min-w-0 items-center gap-1.5 px-1.5 py-1", children: [_jsx("span", { className: ['shrink-0 text-base-content/70', compact ? 'hidden' : ''].join(' '), children: _jsx(Icon, { name: typeIcon, size: "xs", title: room.type === 'break'
|
|
89
|
+
? 'Break room — where you go when you are stepping away'
|
|
90
|
+
: room.type === 'reception'
|
|
91
|
+
? 'Reception — where you arrive'
|
|
92
|
+
: undefined }) }), _jsx("span", { className: ['flex-1 truncate text-xs font-medium', tiny ? 'min-w-0' : 'min-w-[2ch]'].join(' '), title: room.name, children: room.name }), call && !tiny && (_jsxs("span", { role: "img", "aria-label": `Call with ${call.participants.length} ${call.participants.length === 1 ? 'person' : 'people'}${callFull ? ', full' : ''}`, "data-testid": `room-call-${room.id}`, className: [
|
|
93
|
+
'inline-flex shrink-0 items-center gap-0.5 rounded px-1 py-px text-[10px] font-medium tabular-nums',
|
|
94
|
+
callFull ? 'bg-base-200 text-base-content/70' : 'bg-primary/15 text-primary',
|
|
95
|
+
].join(' '), children: [_jsx(Icon, { name: "mic", size: "xs" }), !compact && (_jsxs("span", { "aria-hidden": "true", children: [call.participants.length, "/", call.limit] }))] })), locked && !compact && (_jsx("span", { className: "shrink-0 text-base-content/70", children: _jsx(Icon, { name: "lock", size: "xs", title: `${room.name} is locked` }) })), !compact && (_jsx("span", { className: "shrink-0 text-[10px] tabular-nums text-base-content/60", children: capacity === null ? occupancy : `${occupancy}/${capacity}` })), actions.map((action) => {
|
|
96
|
+
const shared = {
|
|
97
|
+
size: 'xs',
|
|
98
|
+
variant: action.variant,
|
|
99
|
+
// Tighter on a narrow bar, where a worded button has nothing to shrink
|
|
100
|
+
// to: the utility beats the kit's own padding, which lives in the
|
|
101
|
+
// component layer below it.
|
|
102
|
+
className: compact && !action.icon ? 'shrink-0 px-1' : 'shrink-0',
|
|
103
|
+
onClick: action.onClick,
|
|
104
|
+
disabled: action.disabled,
|
|
105
|
+
...(action.disabled && action.reason ? { 'aria-describedby': `${room.id}-why` } : {}),
|
|
106
|
+
};
|
|
107
|
+
/*
|
|
108
|
+
Lock and Unlock are a padlock at every width, with the word as their
|
|
109
|
+
accessible name. Join and Knock are words at every width, with less
|
|
110
|
+
padding around them on a narrow bar. What gives way instead is
|
|
111
|
+
everything else: the head count, the room-type icon and the lock icon on
|
|
112
|
+
a narrow bar, the call badge's count and then the badge itself on the
|
|
113
|
+
tiniest — and the name truncates to its first letters, or on the very
|
|
114
|
+
tiniest to nothing, because the room's action is never the thing that
|
|
115
|
+
goes.
|
|
116
|
+
*/
|
|
117
|
+
return action.icon ? (_jsx(Button, { ...shared, icon: action.icon, "aria-label": action.label }, action.key)) : (_jsx(Button, { ...shared, children: action.label }, action.key));
|
|
118
|
+
})] }), message && (_jsx("p", { id: `${room.id}-why`, className: "border-t border-base-300 bg-base-200/70 px-1.5 py-0.5 text-[10px] leading-snug text-base-content/75", children: message }))] }));
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=RoomBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoomBar.js","sourceRoot":"","sources":["../src/RoomBar.tsx"],"names":[],"mappings":"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"}
|
package/dist/Share.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ScreenSource } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
/**
|
|
3
|
+
* Showing a screen to the people in the call.
|
|
4
|
+
*
|
|
5
|
+
* Four pieces, separate because they appear in different places: the share itself
|
|
6
|
+
* fills the call view, the indicator follows the sharer everywhere including back
|
|
7
|
+
* onto the map, and the two questions are asked once and then gone.
|
|
8
|
+
*
|
|
9
|
+
* Provider-independent, like the tiles: a share here is a stream and a name, and
|
|
10
|
+
* nothing in this file knows whether a mesh or somebody's SFU delivered it.
|
|
11
|
+
*/
|
|
12
|
+
export interface ShareStageProps {
|
|
13
|
+
/** Whose screen it is. Used in the caption and in the accessible name. */
|
|
14
|
+
sharerName: string;
|
|
15
|
+
/** True when it is your own screen, which is drawn completely differently. */
|
|
16
|
+
mine: boolean;
|
|
17
|
+
/** The share, once it arrives. Absent is a loading state and not an error. */
|
|
18
|
+
stream?: MediaStream | null;
|
|
19
|
+
/** The chosen speaker, so a shared tab's sound goes where the voices go. */
|
|
20
|
+
speakerDeviceId?: string;
|
|
21
|
+
/** Offered only on your own share, and never more than one click away. */
|
|
22
|
+
onStop?(): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function ShareStage({ sharerName, mine, stream, speakerDeviceId, onStop }: ShareStageProps): import("react").JSX.Element;
|
|
25
|
+
/**
|
|
26
|
+
* The sharer's own reminder that they are still sharing.
|
|
27
|
+
*
|
|
28
|
+
* Persistent, not dismissible, and present in every view including the map, because
|
|
29
|
+
* forgetting to stop is the commonest failure in any call product and the
|
|
30
|
+
* consequence of it is somebody's inbox on a projector. The stop control is here as
|
|
31
|
+
* well as in the bar so that it is one click from wherever the person has wandered.
|
|
32
|
+
*/
|
|
33
|
+
export declare function SharingBanner({ onStop }: {
|
|
34
|
+
onStop(): void;
|
|
35
|
+
}): import("react").JSX.Element;
|
|
36
|
+
/**
|
|
37
|
+
* Taking over somebody else's share.
|
|
38
|
+
*
|
|
39
|
+
* Asked rather than refused, because the person who wants to show something next is
|
|
40
|
+
* usually right that they do — and asked rather than done quietly, because a share
|
|
41
|
+
* that disappears mid-sentence looks like a crash to whoever was presenting.
|
|
42
|
+
*/
|
|
43
|
+
export declare function TakeOverDialog({ sharerName, onConfirm, onCancel, }: {
|
|
44
|
+
sharerName: string;
|
|
45
|
+
onConfirm(): void;
|
|
46
|
+
onCancel(): void;
|
|
47
|
+
}): import("react").JSX.Element;
|
|
48
|
+
export interface ScreenSourcePickerProps {
|
|
49
|
+
/** The host's list, exactly as it gave it. Filtered here, not by the caller. */
|
|
50
|
+
sources: readonly ScreenSource[];
|
|
51
|
+
onPick(sourceId: string): void;
|
|
52
|
+
onCancel(): void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The picker for a platform whose browser has none.
|
|
56
|
+
*
|
|
57
|
+
* On the web this never appears: `getDisplayMedia` opens the browser's own picker,
|
|
58
|
+
* which people already know and which is the only one that can offer a single tab.
|
|
59
|
+
* A desktop app has no such dialog — the platform hands over a list of screens and
|
|
60
|
+
* windows and expects the app to draw it — so this is that list.
|
|
61
|
+
*
|
|
62
|
+
* Our own window is never in it. Sharing the app that is doing the sharing produces
|
|
63
|
+
* an infinite mirror, and it is dropped by `usableSources` rather than here, so the
|
|
64
|
+
* rule belongs to the list rather than to this one drawing of it.
|
|
65
|
+
*/
|
|
66
|
+
export declare function ScreenSourcePicker({ sources, onPick, onCancel }: ScreenSourcePickerProps): import("react").JSX.Element;
|
|
67
|
+
/**
|
|
68
|
+
* What a live region says about a share.
|
|
69
|
+
*
|
|
70
|
+
* Politely, and named. A share is a change to what is on everybody's screen, and
|
|
71
|
+
* somebody who cannot see it change is precisely the person who needs telling.
|
|
72
|
+
*/
|
|
73
|
+
export declare function describeShare(sharerName: string, sharing: boolean): string;
|
|
74
|
+
//# sourceMappingURL=Share.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Share.d.ts","sourceRoot":"","sources":["../src/Share.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AAIvE;;;;;;;;;GASG;AAEH,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAA;IAClB,8EAA8E;IAC9E,IAAI,EAAE,OAAO,CAAA;IACb,8EAA8E;IAC9E,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,0EAA0E;IAC1E,MAAM,CAAC,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,UAAU,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,eAAe,+BA2FhG;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,IAAI,IAAI,CAAA;CAAE,+BAoB3D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,UAAU,EACV,SAAS,EACT,QAAQ,GACT,EAAE;IACD,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,IAAI,IAAI,CAAA;IACjB,QAAQ,IAAI,IAAI,CAAA;CACjB,+BA2BA;AAED,MAAM,WAAW,uBAAuB;IACtC,gFAAgF;IAChF,OAAO,EAAE,SAAS,YAAY,EAAE,CAAA;IAChC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,QAAQ,IAAI,IAAI,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,uBAAuB,+BA0DxF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAI1E"}
|