@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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Alert, Button, Icon, Modal, Select } from '@unityevolv/unitykit';
|
|
3
|
+
import { labelFor, listDevices, requestPermission, stillAvailable, watchDevices, } from '@unityevolv/ofiskit-realtime-client';
|
|
4
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
|
+
export function DevicePanel(props) {
|
|
6
|
+
const { open, choice, onChoose } = props;
|
|
7
|
+
const [devices, setDevices] = useState({ microphones: [], cameras: [], speakers: [] });
|
|
8
|
+
const [problem, setProblem] = useState(null);
|
|
9
|
+
const [inUseElsewhere, setInUseElsewhere] = useState(false);
|
|
10
|
+
const [granted, setGranted] = useState(false);
|
|
11
|
+
const [level, setLevel] = useState(0);
|
|
12
|
+
const [notice, setNotice] = useState(null);
|
|
13
|
+
const video = useRef(null);
|
|
14
|
+
const stream = useRef(null);
|
|
15
|
+
const audio = useRef(null);
|
|
16
|
+
const stop = useCallback(() => {
|
|
17
|
+
for (const track of stream.current?.getTracks() ?? [])
|
|
18
|
+
track.stop();
|
|
19
|
+
stream.current = null;
|
|
20
|
+
if (audio.current) {
|
|
21
|
+
clearInterval(audio.current.timer);
|
|
22
|
+
void audio.current.context.close().catch(() => { });
|
|
23
|
+
audio.current = null;
|
|
24
|
+
}
|
|
25
|
+
setLevel(0);
|
|
26
|
+
}, []);
|
|
27
|
+
/**
|
|
28
|
+
* Ask for permission, then list.
|
|
29
|
+
*
|
|
30
|
+
* In that order on purpose: device labels are empty until permission has been
|
|
31
|
+
* granted once, and a picker offering "Microphone 1" and "Microphone 2" helps
|
|
32
|
+
* nobody choose their headset.
|
|
33
|
+
*/
|
|
34
|
+
const start = useCallback(async () => {
|
|
35
|
+
const outcome = await requestPermission({ audio: true, video: props.preview ?? false }, choice);
|
|
36
|
+
setGranted(outcome.granted);
|
|
37
|
+
setProblem(outcome.problem ?? null);
|
|
38
|
+
setInUseElsewhere(Boolean(outcome.inUseElsewhere));
|
|
39
|
+
if (outcome.stream) {
|
|
40
|
+
stream.current = outcome.stream;
|
|
41
|
+
if (video.current)
|
|
42
|
+
video.current.srcObject = outcome.stream;
|
|
43
|
+
// A level meter, so nobody joins with a dead microphone and finds out
|
|
44
|
+
// when somebody asks why they have been silent for two minutes.
|
|
45
|
+
try {
|
|
46
|
+
const context = new AudioContext();
|
|
47
|
+
const analyser = context.createAnalyser();
|
|
48
|
+
analyser.fftSize = 256;
|
|
49
|
+
context.createMediaStreamSource(outcome.stream).connect(analyser);
|
|
50
|
+
const samples = new Uint8Array(analyser.frequencyBinCount);
|
|
51
|
+
const timer = setInterval(() => {
|
|
52
|
+
analyser.getByteFrequencyData(samples);
|
|
53
|
+
let total = 0;
|
|
54
|
+
for (const sample of samples)
|
|
55
|
+
total += sample;
|
|
56
|
+
setLevel(Math.min(1, total / samples.length / 128));
|
|
57
|
+
}, 100);
|
|
58
|
+
audio.current = { context, timer };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// No meter is survivable; the picker still works.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
setDevices(await listDevices());
|
|
65
|
+
}, [choice, props.preview]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!open)
|
|
68
|
+
return;
|
|
69
|
+
/*
|
|
70
|
+
* Opening the panel asks the browser for the camera and microphone.
|
|
71
|
+
*
|
|
72
|
+
* This is an effect synchronising with an external system, which is what
|
|
73
|
+
* effects are for: nothing is set until the browser answers, several
|
|
74
|
+
* awaits later. The lint rule cannot see through an async call and reads
|
|
75
|
+
* the setState calls inside `start` as synchronous ones, so it is disabled
|
|
76
|
+
* here with the reason rather than the effect being contorted to please it.
|
|
77
|
+
*/
|
|
78
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
79
|
+
void start();
|
|
80
|
+
// Closing the panel runs this cleanup, which is what stops the camera and
|
|
81
|
+
// the level meter. Doing it in the body as well would be a second stop.
|
|
82
|
+
return stop;
|
|
83
|
+
}, [open, start, stop]);
|
|
84
|
+
/**
|
|
85
|
+
* Follow the operating system when something is plugged in or pulled out.
|
|
86
|
+
*
|
|
87
|
+
* With a notice, never silently: somebody who unplugs a headset mid-sentence
|
|
88
|
+
* needs to know their voice is now going through the laptop microphone, and
|
|
89
|
+
* a silent switch is how people end up talking into a dead device.
|
|
90
|
+
*/
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!open)
|
|
93
|
+
return;
|
|
94
|
+
return watchDevices((next) => {
|
|
95
|
+
setDevices(next);
|
|
96
|
+
const surviving = stillAvailable(choice, next);
|
|
97
|
+
if (choice.audioDeviceId && !surviving.audioDeviceId) {
|
|
98
|
+
setNotice('Your microphone was unplugged. The call switched to the built-in one.');
|
|
99
|
+
onChoose(surviving);
|
|
100
|
+
props.onApply?.(surviving);
|
|
101
|
+
}
|
|
102
|
+
else if (choice.videoDeviceId && !surviving.videoDeviceId) {
|
|
103
|
+
setNotice('Your camera was unplugged.');
|
|
104
|
+
onChoose(surviving);
|
|
105
|
+
props.onApply?.(surviving);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}, [open, choice, onChoose, props]);
|
|
109
|
+
const update = (patch) => {
|
|
110
|
+
const next = { ...choice, ...patch };
|
|
111
|
+
onChoose(next);
|
|
112
|
+
props.onApply?.(next);
|
|
113
|
+
// Stop the current preview before opening the newly chosen device, or two
|
|
114
|
+
// cameras end up running and the meter reads the wrong microphone.
|
|
115
|
+
stop();
|
|
116
|
+
void start();
|
|
117
|
+
};
|
|
118
|
+
return (_jsx(Modal, { open: open, onOpenChange: (next) => {
|
|
119
|
+
if (!next)
|
|
120
|
+
props.onClose();
|
|
121
|
+
}, title: "Microphone, camera and speaker", children: _jsxs("div", { className: "space-y-4", children: [notice && (_jsx(Alert, { variant: "info", onDismiss: () => setNotice(null), children: notice })), problem && (_jsxs(Alert, { variant: inUseElsewhere ? 'warn' : 'danger', children: [_jsx("p", { children: problem }), _jsx(Button, { size: "sm", variant: "ghost", className: "mt-2", onClick: () => void start(), children: "Try again" })] })), props.preview && (_jsx("div", { className: "overflow-hidden rounded-lg bg-base-300", children: _jsx("video", { ref: video, autoPlay: true, playsInline: true, muted: true, "aria-label": "Camera preview", className: "aspect-video w-full scale-x-[-1] object-cover" }) })), _jsxs("div", { children: [_jsxs("p", { className: "mb-1 flex items-center gap-1 text-xs font-medium text-base-content/70", children: [_jsx(Icon, { name: "mic", size: "xs" }), "Microphone level"] }), _jsx("div", { role: "meter", "aria-label": "Microphone level", "aria-valuenow": Math.round(level * 100), "aria-valuemin": 0, "aria-valuemax": 100, className: "h-2 w-full overflow-hidden rounded-full bg-base-300", children: _jsx("div", { className: "h-full rounded-full bg-success transition-[width] duration-100", style: { width: `${Math.round(level * 100)}%` } }) }), granted && level === 0 && (_jsx("p", { className: "mt-1 text-xs text-base-content/70", children: "Say something \u2014 if this stays still, the microphone is not picking you up." }))] }), _jsxs(Select, { label: "Microphone", value: choice.audioDeviceId ?? '', onChange: (event) => update({ audioDeviceId: event.target.value || undefined }), children: [_jsx("option", { value: "", children: "System default" }), devices.microphones.map((device, index) => (_jsx("option", { value: device.deviceId, children: labelFor(device, index) }, device.deviceId)))] }), _jsxs(Select, { label: "Camera", value: choice.videoDeviceId ?? '', onChange: (event) => update({ videoDeviceId: event.target.value || undefined }), children: [_jsx("option", { value: "", children: "System default" }), devices.cameras.map((device, index) => (_jsx("option", { value: device.deviceId, children: labelFor(device, index) }, device.deviceId)))] }), devices.speakers.length > 0 && (_jsxs(Select, { label: "Speaker", value: choice.speakerDeviceId ?? '', onChange: (event) => update({ speakerDeviceId: event.target.value || undefined }), children: [_jsx("option", { value: "", children: "System default" }), devices.speakers.map((device, index) => (_jsx("option", { value: device.deviceId, children: labelFor(device, index) }, device.deviceId)))] })), _jsx("p", { className: "text-xs text-base-content/60", children: "Noise suppression and echo cancellation are on. Your choice is remembered on this device." })] }) }));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Said before the browser's own prompt, not after.
|
|
125
|
+
*
|
|
126
|
+
* The browser asks a blunt question with no context, and somebody who says no
|
|
127
|
+
* to it has a much harder time saying yes later. Explaining first is the
|
|
128
|
+
* difference between a prompt that gets accepted and a support ticket.
|
|
129
|
+
*/
|
|
130
|
+
export function PermissionPrimer({ onContinue }) {
|
|
131
|
+
return (_jsxs("div", { className: "mx-auto max-w-sm rounded-lg bg-base-100 p-4 text-center ring-1 ring-base-300", children: [_jsx(Icon, { name: "mic", size: "lg" }), _jsx("h2", { className: "mt-2 text-lg font-semibold", children: "Let your browser use the microphone" }), _jsx("p", { className: "mt-1 text-sm text-base-content/75", children: "Your browser is about to ask. Nothing is recorded, and nothing is sent anywhere until you press the microphone button in a room." }), _jsx(Button, { className: "mt-3", onClick: onContinue, children: "Continue" })] }));
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=DevicePanel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevicePanel.js","sourceRoot":"","sources":["../src/DevicePanel.tsx"],"names":[],"mappings":"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"}
|
package/dist/Knocks.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Somebody is at the door, and what happens next.
|
|
3
|
+
*
|
|
4
|
+
* Two sides of the same moment. Inside the room, a knock with a name and two
|
|
5
|
+
* buttons. Outside, feedback that the knock was sent and then a clear outcome —
|
|
6
|
+
* because the worst version of this feature is knocking into silence and never
|
|
7
|
+
* learning whether anybody saw it.
|
|
8
|
+
*
|
|
9
|
+
* Admitting lets that one person in without unlocking the room for anybody else,
|
|
10
|
+
* and the card says so, because "let them in" reads like "open the door".
|
|
11
|
+
*/
|
|
12
|
+
export interface IncomingKnock {
|
|
13
|
+
knockId: string;
|
|
14
|
+
roomId: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
displayName: string;
|
|
17
|
+
photoUrl?: string;
|
|
18
|
+
/** True when everybody inside is on do not disturb: it arrived without a sound. */
|
|
19
|
+
silent: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface KnockDockProps {
|
|
22
|
+
knocks: IncomingKnock[];
|
|
23
|
+
onAdmit(knockId: string): void;
|
|
24
|
+
onDecline(knockId: string): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function KnockDock({ knocks, onAdmit, onDecline }: KnockDockProps): import("react").JSX.Element | null;
|
|
27
|
+
export type KnockOutcome = 'waiting' | 'admitted' | 'declined' | 'expired' | 'refused';
|
|
28
|
+
export interface OutgoingKnockProps {
|
|
29
|
+
roomName: string;
|
|
30
|
+
outcome: KnockOutcome;
|
|
31
|
+
/** The reason, when the knock was refused outright — rate limited, say. */
|
|
32
|
+
message?: string | null;
|
|
33
|
+
/** True when it arrived silently, so the knocker knows why it may go unanswered. */
|
|
34
|
+
silent?: boolean;
|
|
35
|
+
onDismiss(): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* What the person who knocked sees.
|
|
39
|
+
*
|
|
40
|
+
* Every outcome is said plainly, **including the one where nobody answered**,
|
|
41
|
+
* because an unanswered knock is otherwise indistinguishable from a broken one.
|
|
42
|
+
* A refusal says why: repeated knocking is rate limited, and a button that
|
|
43
|
+
* appears to do nothing is worse than a button that says no.
|
|
44
|
+
*/
|
|
45
|
+
export declare function OutgoingKnock({ roomName, outcome, message, silent, onDismiss, }: OutgoingKnockProps): import("react").JSX.Element;
|
|
46
|
+
//# sourceMappingURL=Knocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Knocks.d.ts","sourceRoot":"","sources":["../src/Knocks.tsx"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mFAAmF;IACnF,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAED,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,cAAc,sCA4CvE;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;AAEtF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,YAAY,CAAA;IACrB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,SAAS,IAAI,IAAI,CAAA;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,OAAO,EACP,OAAO,EACP,MAAc,EACd,SAAS,GACV,EAAE,kBAAkB,+BAgDpB"}
|
package/dist/Knocks.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Icon } from '@unityevolv/unitykit';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
export function KnockDock({ knocks, onAdmit, onDecline }) {
|
|
5
|
+
if (knocks.length === 0)
|
|
6
|
+
return null;
|
|
7
|
+
return (
|
|
8
|
+
// A section rather than a div: an aria-label on a plain div is exposed to
|
|
9
|
+
// nothing, so the label would have been decoration. As a landmark it is
|
|
10
|
+
// something somebody can jump to while a colleague waits outside.
|
|
11
|
+
_jsx("section", { className: "pointer-events-auto absolute bottom-4 right-4 z-30 flex w-72 flex-col gap-2", "aria-label": "People knocking", children: knocks.map((knock) => (_jsxs("div", { className: "rounded-lg bg-base-100 p-3 shadow-lg ring-1 ring-base-300", "data-testid": `knock-${knock.knockId}`, children: [_jsxs("p", { className: "flex items-center gap-2 text-sm", children: [_jsx(Icon, { name: "knock", size: "sm" }), _jsxs("span", { children: [_jsx("strong", { children: knock.displayName }), " would like to come in."] })] }), _jsxs("div", { className: "mt-2 flex gap-2", children: [_jsx(Button, { size: "sm", onClick: () => onAdmit(knock.knockId), children: "Let them in" }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => onDecline(knock.knockId), children: "Not now" })] }), _jsx("p", { className: "mt-1.5 text-xs text-base-content/60", children: "Letting them in does not unlock the room for anyone else." })] }, knock.knockId))) }));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* What the person who knocked sees.
|
|
15
|
+
*
|
|
16
|
+
* Every outcome is said plainly, **including the one where nobody answered**,
|
|
17
|
+
* because an unanswered knock is otherwise indistinguishable from a broken one.
|
|
18
|
+
* A refusal says why: repeated knocking is rate limited, and a button that
|
|
19
|
+
* appears to do nothing is worse than a button that says no.
|
|
20
|
+
*/
|
|
21
|
+
export function OutgoingKnock({ roomName, outcome, message, silent = false, onDismiss, }) {
|
|
22
|
+
// Anything final clears itself after a few seconds. Only "waiting" stays, for
|
|
23
|
+
// as long as the waiting does.
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (outcome === 'waiting')
|
|
26
|
+
return;
|
|
27
|
+
const timer = setTimeout(onDismiss, 6000);
|
|
28
|
+
return () => clearTimeout(timer);
|
|
29
|
+
}, [outcome, onDismiss]);
|
|
30
|
+
const text = {
|
|
31
|
+
waiting: silent
|
|
32
|
+
? `Knocked on ${roomName}. Everybody inside is on do not disturb, so it arrived silently.`
|
|
33
|
+
: `Knocked on ${roomName}. Waiting for an answer…`,
|
|
34
|
+
admitted: `You were let into ${roomName}.`,
|
|
35
|
+
declined: `Not right now — try ${roomName} again in a bit.`,
|
|
36
|
+
expired: `Nobody answered in ${roomName}.`,
|
|
37
|
+
refused: message ?? 'That knock could not be sent.',
|
|
38
|
+
};
|
|
39
|
+
const tone = outcome === 'admitted'
|
|
40
|
+
? 'bg-success text-success-content'
|
|
41
|
+
: outcome === 'waiting'
|
|
42
|
+
? 'bg-base-100 text-base-content ring-1 ring-base-300'
|
|
43
|
+
: 'bg-base-300 text-base-content';
|
|
44
|
+
return (_jsx("div", { role: "status", "data-testid": "outgoing-knock", className: ['pointer-events-auto rounded-lg px-3 py-2 text-sm shadow-lg', tone].join(' '), children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: outcome === 'admitted' ? 'check' : 'knock', size: "sm" }), _jsx("span", { children: text[outcome] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "ml-auto rounded p-0.5 hover:bg-base-200/50 focus-visible:outline-2 focus-visible:outline-primary", children: _jsx(Icon, { name: "close", size: "xs" }) })] }) }));
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=Knocks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Knocks.js","sourceRoot":"","sources":["../src/Knocks.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { OfficeState } from '@unityevolv/ofiskit-realtime-client';
|
|
2
|
+
import { type Room, type Template } from '@unityevolv/ofiskit-template';
|
|
3
|
+
import type { LiveReaction } from './useCall.js';
|
|
4
|
+
/**
|
|
5
|
+
* The office: the background image with rooms drawn over it, and people in them.
|
|
6
|
+
*
|
|
7
|
+
* The picture is the point of the product, and it is also the hardest thing here
|
|
8
|
+
* to use without sight or a mouse. So the map is a landmark region, every room is
|
|
9
|
+
* a labelled group in reading order, arrow keys move between rooms, Enter joins or
|
|
10
|
+
* knocks, and there is a list view carrying exactly the same state and the same
|
|
11
|
+
* actions for anybody who would rather not have a picture at all.
|
|
12
|
+
*
|
|
13
|
+
* Nothing is drawn directly onto the image. Every bar, avatar and dot is a kit
|
|
14
|
+
* surface with its own contrast, because the image is whatever an author
|
|
15
|
+
* generated and nothing legible can be guaranteed on top of it.
|
|
16
|
+
*/
|
|
17
|
+
export interface OfficeMapProps {
|
|
18
|
+
template: Template;
|
|
19
|
+
state: OfficeState;
|
|
20
|
+
/** Turns a template's image name into something the browser can fetch. */
|
|
21
|
+
imageUrl(name: string): string;
|
|
22
|
+
/** Capacity is an office setting; the free office has none. */
|
|
23
|
+
capacityOf?(room: Room): number | null;
|
|
24
|
+
/**
|
|
25
|
+
* Reactions in the air, by person. From `useReactions`.
|
|
26
|
+
*
|
|
27
|
+
* By person rather than by device here, because an avatar is a person — the same
|
|
28
|
+
* reaction is keyed by device for the tiles, which are screens.
|
|
29
|
+
*/
|
|
30
|
+
reactions?: ReadonlyMap<string, LiveReaction[]>;
|
|
31
|
+
onJoin(roomId: string): void;
|
|
32
|
+
onKnock(roomId: string): void;
|
|
33
|
+
onLock(roomId: string): void;
|
|
34
|
+
onUnlock(roomId: string): void;
|
|
35
|
+
}
|
|
36
|
+
export declare function OfficeMap(props: OfficeMapProps): import("react").JSX.Element;
|
|
37
|
+
/**
|
|
38
|
+
* The same office as a list.
|
|
39
|
+
*
|
|
40
|
+
* The accessible alternative to the map, and genuinely useful on a small laptop
|
|
41
|
+
* where the picture is too small to click. It shows the same state, offers the
|
|
42
|
+
* same actions and updates from the same realtime state, so the two can never
|
|
43
|
+
* disagree — which is the only reason it is safe to offer at all.
|
|
44
|
+
*/
|
|
45
|
+
export declare function RoomListView(props: OfficeMapProps): import("react").JSX.Element;
|
|
46
|
+
/** Which of the two views somebody is looking at. Remembered, because it is a preference. */
|
|
47
|
+
export type OfficeView = 'map' | 'list';
|
|
48
|
+
export declare function ViewToggle({ view, onChange, }: {
|
|
49
|
+
view: OfficeView;
|
|
50
|
+
onChange(view: OfficeView): void;
|
|
51
|
+
}): import("react").JSX.Element;
|
|
52
|
+
/**
|
|
53
|
+
* Hide everything but the office.
|
|
54
|
+
*
|
|
55
|
+
* Exported from here although this app has no chrome to hide: the control belongs
|
|
56
|
+
* to the shared package because the wrapper's app shell is what it hides, and a
|
|
57
|
+
* second copy of it there would be a second set of behaviour to keep right.
|
|
58
|
+
*
|
|
59
|
+
* Somebody who works maximised should not have to set it every session, so the
|
|
60
|
+
* choice is remembered. Escape gets out, because a mode with no visible way back
|
|
61
|
+
* is a trap.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useMaximised(): [boolean, (next: boolean) => void];
|
|
64
|
+
/** The control that toggles it, so the label and the icon stay in one place. */
|
|
65
|
+
export declare function MaximiseButton({ maximised, onToggle, }: {
|
|
66
|
+
maximised: boolean;
|
|
67
|
+
onToggle(): void;
|
|
68
|
+
}): import("react").JSX.Element;
|
|
69
|
+
//# sourceMappingURL=OfficeMap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfficeMap.d.ts","sourceRoot":"","sources":["../src/OfficeMap.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAA4B,MAAM,qCAAqC,CAAA;AAEhG,OAAO,EAAW,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAShF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGhD;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAA;IAClB,KAAK,EAAE,WAAW,CAAA;IAClB,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,+DAA+D;IAC/D,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IACtC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;IAC/C,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAuP9C;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,+BA4DjD;AAuED,6FAA6F;AAC7F,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,CAAA;AAEvC,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,UAAU,CAAA;IAChB,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;CACjC,+BAuBA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC,CAajE;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,IAAI,IAAI,CAAA;CACjB,+BAYA"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Icon } from '@unityevolv/unitykit';
|
|
3
|
+
import { callIn, isLocked, occupancy, peopleIn, yourRoom } from '@unityevolv/ofiskit-realtime-client';
|
|
4
|
+
import { barRect } from '@unityevolv/ofiskit-template';
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
6
|
+
import { OverflowAvatar } from './Overflow.js';
|
|
7
|
+
import { PersonAvatar } from './PersonAvatar.js';
|
|
8
|
+
import { RoomBar } from './RoomBar.js';
|
|
9
|
+
import { fitCanvas, readingOrder, roomInDirection, toPixels } from './layout.js';
|
|
10
|
+
import { placeInRoom } from './placement.js';
|
|
11
|
+
import { useMeasured, usePersisted, useReducedMotion } from './hooks.js';
|
|
12
|
+
import { useTheme } from './theme.js';
|
|
13
|
+
export function OfficeMap(props) {
|
|
14
|
+
const { template, state, imageUrl, capacityOf, reactions } = props;
|
|
15
|
+
const { theme } = useTheme();
|
|
16
|
+
const reducedMotion = useReducedMotion();
|
|
17
|
+
// Measured rather than guessed: the map shares its space with the tile strip,
|
|
18
|
+
// which appears and disappears as calls start and end.
|
|
19
|
+
const [container, box] = useMeasured();
|
|
20
|
+
const [focused, setFocused] = useState(null);
|
|
21
|
+
const canvas = fitCanvas(template.canvas, box);
|
|
22
|
+
const ordered = readingOrder(template.rooms);
|
|
23
|
+
const yourRoomId = yourRoom(state);
|
|
24
|
+
/**
|
|
25
|
+
* The template's dark image in dark mode, the light one otherwise.
|
|
26
|
+
*
|
|
27
|
+
* Swapped in place without reloading the office, so changing theme mid-call is a
|
|
28
|
+
* repaint rather than an interruption. Theme is per person, so two people in the
|
|
29
|
+
* same room may be looking at different pictures over identical geometry.
|
|
30
|
+
*/
|
|
31
|
+
const background = theme === 'dark' && template.images.dark ? template.images.dark : template.images.light;
|
|
32
|
+
const onKeyDown = useCallback((event, room) => {
|
|
33
|
+
/*
|
|
34
|
+
* Only keys pressed on the room itself.
|
|
35
|
+
*
|
|
36
|
+
* The room is a focusable group with controls inside it — Join, Lock, Knock,
|
|
37
|
+
* the overflow counter — and a key pressed on any of them bubbles up to here.
|
|
38
|
+
* Handling it here as well turned Enter on Lock into nothing, Enter on the
|
|
39
|
+
* counter into walking into the room, and the arrow keys inside the counter's
|
|
40
|
+
* list into jumping to the next room. React carries events up through
|
|
41
|
+
* portals too, so the counter's popover is inside this group as far as this
|
|
42
|
+
* handler is concerned, even though it is drawn somewhere else in the page.
|
|
43
|
+
*/
|
|
44
|
+
if (event.target !== event.currentTarget)
|
|
45
|
+
return;
|
|
46
|
+
const directions = {
|
|
47
|
+
ArrowLeft: 'left',
|
|
48
|
+
ArrowRight: 'right',
|
|
49
|
+
ArrowUp: 'up',
|
|
50
|
+
ArrowDown: 'down',
|
|
51
|
+
};
|
|
52
|
+
const direction = directions[event.key];
|
|
53
|
+
if (direction) {
|
|
54
|
+
const next = roomInDirection(ordered, room, direction);
|
|
55
|
+
if (next) {
|
|
56
|
+
event.preventDefault();
|
|
57
|
+
setFocused(next.id);
|
|
58
|
+
container.current?.querySelector(`[data-room="${next.id}"]`)?.focus();
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
if (room.id === yourRoomId)
|
|
65
|
+
return;
|
|
66
|
+
if (isLocked(state, room.id))
|
|
67
|
+
props.onKnock(room.id);
|
|
68
|
+
else
|
|
69
|
+
props.onJoin(room.id);
|
|
70
|
+
}
|
|
71
|
+
}, [container, ordered, state, yourRoomId, props]);
|
|
72
|
+
return (_jsxs("section", { ref: container, "aria-label": `${template.name} office map`, className: "relative h-full w-full overflow-hidden bg-base-200", children: [!state.ready && (_jsx("p", { className: "absolute inset-x-0 top-1/2 text-center text-sm text-base-content/60", role: "status", children: "Looking around the office\u2026" })), canvas.width > 0 && state.ready && (_jsxs(_Fragment, { children: [_jsx("img", { src: imageUrl(background), alt: "", "aria-hidden": "true", draggable: false, className: "absolute select-none", style: {
|
|
73
|
+
left: canvas.left,
|
|
74
|
+
top: canvas.top,
|
|
75
|
+
width: canvas.width,
|
|
76
|
+
height: canvas.height,
|
|
77
|
+
} }), ordered.map((room) => {
|
|
78
|
+
const pixels = toPixels(room.rect, canvas);
|
|
79
|
+
const people = peopleIn(state, room.id);
|
|
80
|
+
const locked = isLocked(state, room.id);
|
|
81
|
+
const capacity = capacityOf?.(room) ?? null;
|
|
82
|
+
const inside = room.id === yourRoomId;
|
|
83
|
+
const count = occupancy(state, room.id);
|
|
84
|
+
const call = callIn(state, room.id);
|
|
85
|
+
const placement = placeInRoom(room, people, template.canvas, template.avatarSize);
|
|
86
|
+
const bar = toPixels(barRect(room.rect, room.bar, template.canvas), canvas);
|
|
87
|
+
return (_jsxs("div", { "data-room": room.id, role: "group", tabIndex: 0, "aria-label": [
|
|
88
|
+
room.name,
|
|
89
|
+
room.type === 'break' ? 'break room' : room.type,
|
|
90
|
+
`${count} ${count === 1 ? 'person' : 'people'}`,
|
|
91
|
+
capacity === null ? '' : `of ${capacity}`,
|
|
92
|
+
// Announced while arrowing between rooms, which is how somebody
|
|
93
|
+
// who cannot see the map decides where to go. Without it the call
|
|
94
|
+
// is drawn on the bar and said nowhere.
|
|
95
|
+
call
|
|
96
|
+
? `call with ${call.participants.length} ${call.participants.length === 1 ? 'person' : 'people'}${call.participants.length >= call.limit ? ', full' : ''}`
|
|
97
|
+
: '',
|
|
98
|
+
locked ? 'locked' : 'open',
|
|
99
|
+
inside ? 'you are here' : locked ? 'press Enter to knock' : 'press Enter to join',
|
|
100
|
+
]
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.join(', '), onKeyDown: (event) => onKeyDown(event, room), onFocus: () => setFocused(room.id), className: [
|
|
103
|
+
'absolute rounded-md outline-offset-2',
|
|
104
|
+
'focus-visible:outline-2 focus-visible:outline-primary',
|
|
105
|
+
inside ? 'ring-2 ring-primary' : 'ring-1 ring-base-content/10',
|
|
106
|
+
focused === room.id ? 'z-20' : 'z-10',
|
|
107
|
+
].join(' '), style: {
|
|
108
|
+
left: pixels.left,
|
|
109
|
+
top: pixels.top,
|
|
110
|
+
width: pixels.width,
|
|
111
|
+
height: pixels.height,
|
|
112
|
+
}, children: [_jsx("div", { className: "pointer-events-none absolute z-10", style: { left: 0, top: bar.top - pixels.top, width: pixels.width }, children: _jsx(RoomBar, { room: room, occupancy: count, capacity: capacity, locked: locked, inside: inside, call: call, width: pixels.width, onJoin: () => props.onJoin(room.id), onKnock: () => props.onKnock(room.id), onLock: () => props.onLock(room.id), onUnlock: () => props.onUnlock(room.id) }) }), _jsxs("ul", { className: "contents", "aria-label": `People in ${room.name}`, children: [placement.placed.map(({ token, rect }) => {
|
|
113
|
+
const cell = toPixels(rect, canvas);
|
|
114
|
+
return (_jsx("li", { className: "absolute flex items-start justify-center", style: {
|
|
115
|
+
left: cell.left - pixels.left,
|
|
116
|
+
top: cell.top - pixels.top,
|
|
117
|
+
width: cell.width,
|
|
118
|
+
height: cell.height,
|
|
119
|
+
// A move is a position change, so the browser animates
|
|
120
|
+
// it and the eye can follow who went where. Reduced
|
|
121
|
+
// motion means they simply appear in the new room.
|
|
122
|
+
transition: reducedMotion ? undefined : 'left 220ms ease, top 220ms ease',
|
|
123
|
+
}, children: _jsx(PersonAvatar, { person: token.person, size: cell.width, ...(token.deviceId ? { deviceId: token.deviceId } : {}), linked: token.linked, reducedMotion: reducedMotion, reactions: reactions?.get(token.person.userId) ?? [] }) }, token.key));
|
|
124
|
+
}), placement.overflowAt &&
|
|
125
|
+
(() => {
|
|
126
|
+
const cell = toPixels(placement.overflowAt, canvas);
|
|
127
|
+
return (_jsx("li", { className: "absolute flex items-start justify-center", style: {
|
|
128
|
+
left: cell.left - pixels.left,
|
|
129
|
+
top: cell.top - pixels.top,
|
|
130
|
+
width: cell.width,
|
|
131
|
+
height: cell.height,
|
|
132
|
+
}, children: _jsx(OverflowAvatar, { roomName: room.name, tokens: placement.overflow, size: cell.width, reducedMotion: reducedMotion, ...(reactions ? { reactions } : {}) }) }));
|
|
133
|
+
})()] })] }, room.id));
|
|
134
|
+
})] }))] }));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The same office as a list.
|
|
138
|
+
*
|
|
139
|
+
* The accessible alternative to the map, and genuinely useful on a small laptop
|
|
140
|
+
* where the picture is too small to click. It shows the same state, offers the
|
|
141
|
+
* same actions and updates from the same realtime state, so the two can never
|
|
142
|
+
* disagree — which is the only reason it is safe to offer at all.
|
|
143
|
+
*/
|
|
144
|
+
export function RoomListView(props) {
|
|
145
|
+
const { template, state, capacityOf, reactions } = props;
|
|
146
|
+
const yourRoomId = yourRoom(state);
|
|
147
|
+
const reducedMotion = useReducedMotion();
|
|
148
|
+
if (!state.ready) {
|
|
149
|
+
return (_jsx("nav", { "aria-label": `${template.name} rooms`, className: "h-full overflow-auto p-2", children: _jsx("p", { className: "p-2 text-sm text-base-content/60", role: "status", children: "Looking around the office\u2026" }) }));
|
|
150
|
+
}
|
|
151
|
+
/*
|
|
152
|
+
* The two rooms every office has come first, side by side, and then everything
|
|
153
|
+
* else in the order the map reads.
|
|
154
|
+
*
|
|
155
|
+
* Reception is where everybody arrives and the break room is where they go to
|
|
156
|
+
* step away, so they are the two rooms somebody looks for without knowing the
|
|
157
|
+
* office — and they are the same two in every office, which makes the top of the
|
|
158
|
+
* list the one part of it that never moves. Placed first rather than wherever the
|
|
159
|
+
* author drew them, so a phone shows them without scrolling.
|
|
160
|
+
*/
|
|
161
|
+
const ordered = readingOrder(template.rooms);
|
|
162
|
+
const shared = ['reception', 'break'].flatMap((type) => ordered.filter((room) => room.type === type));
|
|
163
|
+
const rest = ordered.filter((room) => room.type !== 'reception' && room.type !== 'break');
|
|
164
|
+
const card = (room, half) => (_jsx(RoomCard, { room: room, half: half, people: peopleIn(state, room.id), locked: isLocked(state, room.id), inside: room.id === yourRoomId, call: callIn(state, room.id), capacity: capacityOf?.(room) ?? null, reducedMotion: reducedMotion, ...(reactions ? { reactions } : {}), onJoin: () => props.onJoin(room.id), onKnock: () => props.onKnock(room.id), onLock: () => props.onLock(room.id), onUnlock: () => props.onUnlock(room.id) }, room.id));
|
|
165
|
+
return (_jsx("nav", { "aria-label": `${template.name} rooms`, className: "h-full overflow-auto p-2", children: _jsxs("ul", { className: "grid grid-cols-2 gap-2", children: [shared.map((room) => card(room, true)), rest.map((room) => card(room, false))] }) }));
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* One room in the list: its bar, and the people in it.
|
|
169
|
+
*
|
|
170
|
+
* A half-width card measures itself, because on a narrow phone half the screen is
|
|
171
|
+
* too little for a room bar's full form: without the compact one, the name is what
|
|
172
|
+
* gets crushed. A full-width card never needs to — it is the width somebody chose
|
|
173
|
+
* this view for, and there is no room rectangle constraining it.
|
|
174
|
+
*/
|
|
175
|
+
function RoomCard(props) {
|
|
176
|
+
const { room, half, people } = props;
|
|
177
|
+
const [measured, size] = useMeasured();
|
|
178
|
+
// Until the first measurement arrives, assume there is room: a bar that starts in
|
|
179
|
+
// its full form and tightens is better than one that starts cramped.
|
|
180
|
+
const width = half && size.width > 0 ? size.width : Number.MAX_SAFE_INTEGER;
|
|
181
|
+
return (_jsxs("li", { ref: measured, className: [
|
|
182
|
+
'min-w-0 rounded-lg bg-base-100 p-2 ring-1 ring-base-300',
|
|
183
|
+
half ? '' : 'col-span-2',
|
|
184
|
+
].join(' '), children: [_jsx(RoomBar, { room: room, occupancy: people.length, capacity: props.capacity, locked: props.locked, inside: props.inside, call: props.call, width: width, onJoin: props.onJoin, onKnock: props.onKnock, onLock: props.onLock, onUnlock: props.onUnlock }), people.length > 0 && (_jsx("ul", { className: "mt-2 flex flex-wrap gap-3 px-1", "aria-label": `People in ${room.name}`, children: people.map((person) => (_jsx("li", { children: _jsx(PersonAvatar, { person: person, size: 56, reducedMotion: props.reducedMotion, reactions: props.reactions?.get(person.userId) ?? [] }) }, person.userId))) }))] }));
|
|
185
|
+
}
|
|
186
|
+
export function ViewToggle({ view, onChange, }) {
|
|
187
|
+
return (_jsx("div", { className: "inline-flex rounded-md ring-1 ring-base-300", role: "group", "aria-label": "Office view", children: ['map', 'list'].map((candidate) => (_jsxs("button", { type: "button", onClick: () => onChange(candidate), "aria-pressed": view === candidate, className: [
|
|
188
|
+
'inline-flex min-h-8 items-center gap-1 px-2 py-1 text-xs first:rounded-l-md last:rounded-r-md',
|
|
189
|
+
'focus-visible:outline-2 focus-visible:outline-primary',
|
|
190
|
+
view === candidate ? 'bg-primary text-primary-content' : 'hover:bg-base-200',
|
|
191
|
+
].join(' '), children: [_jsx(Icon, { name: candidate === 'map' ? 'office' : 'menu', size: "xs" }), _jsx("span", { className: "max-sm:sr-only", children: candidate === 'map' ? 'Map' : 'List' })] }, candidate))) }));
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Hide everything but the office.
|
|
195
|
+
*
|
|
196
|
+
* Exported from here although this app has no chrome to hide: the control belongs
|
|
197
|
+
* to the shared package because the wrapper's app shell is what it hides, and a
|
|
198
|
+
* second copy of it there would be a second set of behaviour to keep right.
|
|
199
|
+
*
|
|
200
|
+
* Somebody who works maximised should not have to set it every session, so the
|
|
201
|
+
* choice is remembered. Escape gets out, because a mode with no visible way back
|
|
202
|
+
* is a trap.
|
|
203
|
+
*/
|
|
204
|
+
export function useMaximised() {
|
|
205
|
+
const [maximised, setMaximised] = usePersisted('ofiskit:maximised', false);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (!maximised)
|
|
208
|
+
return;
|
|
209
|
+
const onKey = (event) => {
|
|
210
|
+
if (event.key === 'Escape')
|
|
211
|
+
setMaximised(false);
|
|
212
|
+
};
|
|
213
|
+
globalThis.addEventListener('keydown', onKey);
|
|
214
|
+
return () => globalThis.removeEventListener('keydown', onKey);
|
|
215
|
+
}, [maximised, setMaximised]);
|
|
216
|
+
return [maximised, setMaximised];
|
|
217
|
+
}
|
|
218
|
+
/** The control that toggles it, so the label and the icon stay in one place. */
|
|
219
|
+
export function MaximiseButton({ maximised, onToggle, }) {
|
|
220
|
+
return (_jsxs("button", { type: "button", onClick: onToggle, className: "inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm hover:bg-base-200 focus-visible:outline-2 focus-visible:outline-primary", "aria-pressed": maximised, children: [_jsx(Icon, { name: maximised ? 'minimize' : 'maximize', size: "sm" }), maximised ? 'Restore' : 'Maximise'] }));
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=OfficeMap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OfficeMap.js","sourceRoot":"","sources":["../src/OfficeMap.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Token } from './placement.js';
|
|
2
|
+
import type { LiveReaction } from './useCall.js';
|
|
3
|
+
/**
|
|
4
|
+
* The people a room has no cell for.
|
|
5
|
+
*
|
|
6
|
+
* User areas are display slots, not seats, so a room can hold more people than it
|
|
7
|
+
* has cells — and when it does, the last cell counts the rest. The counter is drawn
|
|
8
|
+
* **as an avatar** rather than as a badge: it takes a person's place in the room,
|
|
9
|
+
* it sits in a person's cell, and the thing it stands for is people. A small pill
|
|
10
|
+
* in a row of faces reads as a notification rather than as four more colleagues.
|
|
11
|
+
*
|
|
12
|
+
* Opening it shows those people the same way the room shows everybody else: as
|
|
13
|
+
* avatars, with their status and whatever they are doing in the call, three
|
|
14
|
+
* across and three down, scrolling for the rest. A list of names would be the one
|
|
15
|
+
* place in the office where people stopped looking like people.
|
|
16
|
+
*/
|
|
17
|
+
/** Three across and three down, then it scrolls. */
|
|
18
|
+
export declare const OVERFLOW_COLUMNS = 3;
|
|
19
|
+
export declare const OVERFLOW_ROWS = 3;
|
|
20
|
+
/**
|
|
21
|
+
* How tall the list may be.
|
|
22
|
+
*
|
|
23
|
+
* Three rows, and the rest scroll: a room of forty people is a panel of nine faces
|
|
24
|
+
* and a scrollbar, not a panel taller than the map. Fewer when the screen has less
|
|
25
|
+
* room than that on either side of the counter — Radix measures what is available,
|
|
26
|
+
* and a short phone may have less than three rows' worth above or below. The
|
|
27
|
+
* subtraction is the panel's own padding.
|
|
28
|
+
*/
|
|
29
|
+
export declare const OVERFLOW_MAX_HEIGHT = "min(224px, calc(var(--radix-popover-content-available-height, 100vh) - 3.5rem))";
|
|
30
|
+
export interface OverflowAvatarProps {
|
|
31
|
+
roomName: string;
|
|
32
|
+
/** Everyone the counter stands for, in arrival order. */
|
|
33
|
+
tokens: readonly Token[];
|
|
34
|
+
/** The room's cell width, so the counter is the size of the faces beside it. */
|
|
35
|
+
size: number;
|
|
36
|
+
reducedMotion?: boolean;
|
|
37
|
+
/** Reactions by person. The same map the room's avatars read. */
|
|
38
|
+
reactions?: ReadonlyMap<string, LiveReaction[]>;
|
|
39
|
+
}
|
|
40
|
+
export declare function OverflowAvatar({ roomName, tokens, size, reducedMotion, reactions, }: OverflowAvatarProps): import("react").JSX.Element;
|
|
41
|
+
//# sourceMappingURL=Overflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Overflow.d.ts","sourceRoot":"","sources":["../src/Overflow.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD;;;;;;;;;;;;;GAaG;AAEH,oDAAoD;AACpD,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,aAAa,IAAI,CAAA;AAY9B;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,oFAAmI,CAAA;AAenK,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAA;IAChB,yDAAyD;IACzD,MAAM,EAAE,SAAS,KAAK,EAAE,CAAA;IACxB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,iEAAiE;IACjE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;CAChD;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,aAAqB,EACrB,SAAS,GACV,EAAE,mBAAmB,+BA8ErB"}
|