diff --git a/examples/breakout-rooms/src/components/Avatar/Avatar.css b/examples/breakout-rooms/src/components/Avatar/Avatar.css index 080f656fc..af0b02e43 100644 --- a/examples/breakout-rooms/src/components/Avatar/Avatar.css +++ b/examples/breakout-rooms/src/components/Avatar/Avatar.css @@ -1,13 +1,13 @@ .avatar { border-radius: 100%; flex-shrink: 0; - background: #817f99; + background: var(--indigo600); box-shadow: 0 0 0 6px var(--color, transparent); - outline: 4px solid #fff; + outline: 4px solid var(--white); width: 2.2em; height: 2.2em; font-size: 14px; - color: #fff; + color: var(--white); display: flex; justify-content: center; align-items: center; diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css index 9cfcc545f..b590b0c02 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css @@ -2,36 +2,48 @@ display: flex; height: 100%; flex-direction: column; - gap: 1em; + gap: var(--space-small); } .validation-messages { - background: #f5f5f3; - padding: 1em; + background: var(--indigo50); + border-radius: var(--border-radius-medium); + padding: var(--space-medium); } -.validation-messages .validatino-messages-title { +.validation-messages .validation-messages-title { font-size: 1em; font-weight: 600; - line-height: 1em; + line-height: var(--space-small); margin: 0; - color: #656b81; + color: var(--indigo600); } -.validation-messages .validatino-messages-items { +.validation-messages .validation-messages-list { font-size: 0.9em; + padding-left: var(--space-small); + margin-bottom: 0; +} + +.validation-messages-item + .validation-messages-item { + margin-top: var(--space-xsmall); +} + +.validation-messages-item::marker { + color: var(--indigo500); + padding-left: var(--space-small); } .toolbar { display: flex; flex-direction: column; justify-content: center; - gap: 1em; + gap: var(--space-small); } .toolbar button { justify-content: center; margin: 0; width: 100%; - padding: 1em; + padding: var(--space-small); } diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx index 1d42d9f89..b255c0661 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx @@ -68,13 +68,13 @@ export const BreakoutManager: React.FC = () => { if (frame) { await service.setRoomTarget(selectedRoom, frame.id); await miro.board.notifications.showInfo( - `Frame "${frame.title}" has been selected as starting point.`, + `${frame.title} is set as starting point for the room`, ); await miro.board.deselect({ id: frame.id }); setSelectedRoom(undefined); } else { await miro.board.notifications.showError( - "We only support frames as starting point for now", + "Only frames are supported as starting point for rooms", ); } }; @@ -87,7 +87,7 @@ export const BreakoutManager: React.FC = () => { const handleNudge = async (currentUser?: Json) => { if (isUser(currentUser)) { await miro.board.notifications.showInfo( - `<strong>${currentUser?.name}</strong> is waiting to start the session`, + `<strong>${currentUser?.name}</strong> is waiting for you to start the session`, ); } }; @@ -111,7 +111,7 @@ export const BreakoutManager: React.FC = () => { setSelectedRoom(selected); await miro.board.notifications.showInfo( - `Select the frame in the board for ${selected.name}`, + `Set a frame to the room by clicking on the frame title`, ); }; @@ -183,25 +183,31 @@ export const BreakoutManager: React.FC = () => { }); }; + const valRooms = "Add rooms to your session"; + const valUsers = "Add users to each room"; + const valFrames = "Set a frame to each room"; + const validations: string[] = []; if (!breakout?.rooms.length) { - validations.push("Add rooms to your session"); + validations.push(valRooms); + validations.push(valUsers); + validations.push(valFrames); } const allRoomsWithParticipants = breakout?.rooms.every( (room) => room.participants.length > 0, ); - if (!allRoomsWithParticipants) { - validations.push("Add users to each room"); + if (!allRoomsWithParticipants && !validations.includes(valUsers)) { + validations.push(valUsers); } const allRoomsWithTargets = breakout?.rooms.every((room) => Boolean(room.targetId), ); - if (!allRoomsWithTargets) { - validations.push("Set a frame to each room"); + if (!allRoomsWithTargets && !validations.includes(valFrames)) { + validations.push(valFrames); } const canStartSession = validations.length < 1; @@ -239,12 +245,14 @@ export const BreakoutManager: React.FC = () => { {isEditable && validations.length > 0 ? ( <div className="validation-messages"> - <h5 className="validatino-messages-title"> + <h5 className="validation-messages-title"> Before starting the session: </h5> - <ul className="validatino-messages-items"> + <ul className="validation-messages-list"> {validations.map((message) => ( - <li key={message}>{message}</li> + <li className="validation-messages-item" key={message}> + {message} + </li> ))} </ul> </div> @@ -253,10 +261,10 @@ export const BreakoutManager: React.FC = () => { {breakout?.state === "started" ? ( <Button onClick={() => handleStopSession()} - variant="solid-danger" + variant="outline-danger" size="x-large" > - Stop session + Finish session {canUseTimer && timer.state === "started" ? ` (${formatDisplayTime(timer.restDuration)})` : null} diff --git a/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.css b/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.css index 164ab0bd8..c672b87f7 100644 --- a/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.css +++ b/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.css @@ -11,21 +11,46 @@ } .starter-action { - background: #f2f4fc; - border-radius: 0.25em; - padding: 1em; + background: var(--blue300); + border: none; + border-radius: var(--border-radius-medium); + cursor: default; display: flex; - gap: 1em; + padding: var(--space-small); + gap: var(--space-small); align-items: center; align-self: flex-start; width: 100%; } .starter-action .starter-action-title { - font-size: 1em; + font-size: 1.25em; font-weight: 600; line-height: 1.5em; text-align: left; - color: #3859ff; + color: var(--blue700); margin: 0; } + +.starter-action:hover { + background: var(--blue400); + cursor: pointer; +} + +.starter-action:hover .starter-action-title { + color: var(--blue900); +} + +.starter-action:hover .rounded-plus { + background-color: var(--blue900); +} + +.rounded-plus { + border-radius: var(--border-radius-circle); + background-color: var(--blue700); + padding: var(--space-xsmall); +} + +.rounded-plus svg { + color: var(--white); +} diff --git a/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.tsx b/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.tsx index ca98966d1..4dbc949dc 100644 --- a/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.tsx +++ b/examples/breakout-rooms/src/components/BreakoutStarter/BreakoutStarter.tsx @@ -1,4 +1,4 @@ -import { IconButton, IconPlus } from "@mirohq/design-system"; +import { IconPlus } from "@mirohq/design-system"; import React from "react"; import { WaitingIcon } from "../WaitingIcon"; @@ -12,17 +12,12 @@ type Props = { export const BreakoutStarter: React.FC<Props> = ({ onAddGroup }) => { return ( <section className="breakout-starter"> - <div className="starter-action"> - <IconButton - label="Add a room" - variant="solid-prominent" - css={{ borderRadius: "100%" }} - onClick={onAddGroup} - > + <button onClick={onAddGroup} className="starter-action"> + <div className="rounded-plus"> <IconPlus /> - </IconButton> + </div> <h5 className="starter-action-title">Create a room to get started</h5> - </div> + </button> <WaitingIcon /> </section> diff --git a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css index 2fb38c70b..40b902aff 100644 --- a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css +++ b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css @@ -2,12 +2,12 @@ display: flex; flex-direction: column; justify-content: center; - background: #eeeded; - border-radius: 0.25em; + background: var(--indigo50); + border-radius: var(--border-radius-medium); gap: 0.5em; - padding: 1em; + padding: var(--space-small); + padding-top: var(--space-xsmall); width: 100%; - height: 8em; } .room-controls { @@ -43,13 +43,16 @@ .users .list { /** Overrides DS specific tokens for dropdown **/ --space-inset-100: 16px; - font-size: 12px; +} + +.users .list .separator { + margin: var(--space-xsmall) var(--space-xxsmall); } .users .list .item { display: flex; align-items: center; - gap: 1em; + gap: var(--space-small); } .users .list .item + div { padding-left: 3.5em; @@ -57,6 +60,7 @@ .avatars { display: flex; + padding-left: var(--space-xxsmall); } .avatars .avatar:not(:first-child) { diff --git a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx index 6ab4d318a..ca7ccb960 100644 --- a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx +++ b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx @@ -8,7 +8,7 @@ import { IconTrash, IconUserAdd, } from "@mirohq/design-system"; -import { OnlineUserInfo } from "@mirohq/websdk-types"; +import { Frame, OnlineUserInfo } from "@mirohq/websdk-types"; import type { Participant, Room } from "../../types"; import { Avatar } from "../Avatar"; @@ -35,6 +35,24 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ onAddParticipant, onRemoveParticipant, }) => { + const [title, setName] = React.useState(""); + React.useEffect(() => { + async function FetchFrame(): Promise<void> { + if (!room.targetId) { + return; + } + const frame = (await miro.board.getById(room.targetId)) as Frame; + setName(frame?.title); + } + FetchFrame(); + }, [room.targetId]); + + const renderedTitle = title ? `${title}` : "frame"; + const dropdownSeparator = ( + <div className="separator"> + <DropdownMenu.Separator /> + </div> + ); return ( <div key={room.id} className="room"> <div className="room-controls"> @@ -42,8 +60,12 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ {room.name} </h3> <IconButton - label="Select frame" - variant="ghost" + label={ + room.targetId + ? `Room is set to ${renderedTitle}` + : "Set frame to room" + } + variant={room.targetId ? "outline" : "ghost"} disabled={!isEditable} onClick={() => onSelectTarget(room)} > @@ -51,7 +73,7 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ </IconButton> <IconButton - label="Remove frame" + label="Remove room" variant="ghost" disabled={!isEditable} onClick={() => onRemove(room)} @@ -72,7 +94,7 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ <DropdownMenu> <DropdownMenu.Trigger asChild> <IconButton - label="Assign participant" + label="Add user" variant="outline" css={{ borderRadius: "100%" }} > @@ -88,7 +110,7 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ {unassignedUsers.length ? ( <div className="list"> <DropdownMenu.Item disabled> - Users not in the room + Users not in rooms </DropdownMenu.Item> {unassignedUsers.map((user) => ( <DropdownMenu.Item @@ -107,7 +129,7 @@ export const RoomConfig: React.FunctionComponent<Props> = ({ {room.participants.length ? ( <div className="list"> - {unassignedUsers.length ? <DropdownMenu.Separator /> : null} + {unassignedUsers.length ? dropdownSeparator : null} {room.participants.map((user) => ( <DropdownMenu.Item key={user.id} diff --git a/examples/breakout-rooms/src/components/RoomsManager/RoomsManager.css b/examples/breakout-rooms/src/components/RoomsManager/RoomsManager.css index 49bc29d37..88e56c1ea 100644 --- a/examples/breakout-rooms/src/components/RoomsManager/RoomsManager.css +++ b/examples/breakout-rooms/src/components/RoomsManager/RoomsManager.css @@ -1,7 +1,7 @@ .container { display: flex; flex-direction: column; - gap: 1em; + gap: var(--space-small); align-items: center; flex: 1; } @@ -9,8 +9,6 @@ .rooms-container { display: flex; flex-direction: column; - max-height: 24em; - min-height: 10em; gap: 0.5em; overflow: auto; width: 100%; @@ -20,7 +18,7 @@ width: 100%; display: flex; justify-content: flex-start; - gap: 1em; + gap: var(--space-xsmall); align-items: center; } diff --git a/examples/breakout-rooms/src/components/Timer/Timer.css b/examples/breakout-rooms/src/components/Timer/Timer.css index 046f80441..e139a0e86 100644 --- a/examples/breakout-rooms/src/components/Timer/Timer.css +++ b/examples/breakout-rooms/src/components/Timer/Timer.css @@ -10,7 +10,6 @@ align-items: center; gap: 0.5em; padding-right: 0.5em; - border-right: 1px solid var(--indigo400); } .timer-display { diff --git a/examples/breakout-rooms/src/components/Timer/Timer.tsx b/examples/breakout-rooms/src/components/Timer/Timer.tsx index c18565c17..64dee9a11 100644 --- a/examples/breakout-rooms/src/components/Timer/Timer.tsx +++ b/examples/breakout-rooms/src/components/Timer/Timer.tsx @@ -23,11 +23,19 @@ export const DEFAULT_MIN_TIME = convertTime(1, "milliseconds", "minutes"); /** * 10 mins */ -export const DEFAULT_MAX_TIME = convertTime(10, "milliseconds", "minutes"); +export const DEFAULT_MAX_TIME = convertTime(90, "milliseconds", "minutes"); /** - * 15 seconds + * 30 seconds */ -export const DEFAULT_STEP = convertTime(15, "milliseconds", "seconds"); +export const DEFAULT_STEP = convertTime(30, "milliseconds", "seconds"); +/** + * 5 mins + */ +export const BIG_STEP = convertTime(5, "milliseconds", "minutes"); +/** + * 5 mins + */ +export const BIG_STEP_MAX_TIME = convertTime(86, "milliseconds", "minutes"); type Props = { /** @@ -46,6 +54,14 @@ type Props = { * Inc/Dec step in milliseconds */ step?: number; + /** + * Inc/Dec step in milliseconds + */ + bigStep?: number; + /** + * Inc/Dec step in milliseconds + */ + bigStepMaxTime?: number; /** * Set chosen time in milliseconds */ @@ -57,6 +73,8 @@ export const Timer: React.FunctionComponent<Props> = ({ minTime = DEFAULT_MIN_TIME, maxTime = DEFAULT_MAX_TIME, step = DEFAULT_STEP, + bigStep = BIG_STEP, + bigStepMaxTime = BIG_STEP_MAX_TIME, onSet, }) => { const [time, setTime] = React.useState<number>(defaultTime); @@ -70,21 +88,27 @@ export const Timer: React.FunctionComponent<Props> = ({ setTime((time) => (time > minTime ? time - step : time)); }; + const handleBigAdd = () => { + setTime((time) => (time <= bigStepMaxTime ? time + bigStep : time)); + }; + const handleSetTime = () => { onSet(time); setTimeChanged(true); }; + const minutes = formatTime(time) > 1 ? "minutes" : "minute"; + return ( <> - <DropdownMenu> + <DropdownMenu onClose={() => handleSetTime()}> <DropdownMenu.Trigger asChild> <Button variant="outline-prominent" rounded> <Button.IconSlot> <IconTimer /> </Button.IconSlot> <Button.Label> - {timeChanged ? formatTime(time) : "Set timer"} + {timeChanged ? `${formatTime(time)} ${minutes}` : "Set timer"} </Button.Label> </Button> </DropdownMenu.Trigger> @@ -92,8 +116,8 @@ export const Timer: React.FunctionComponent<Props> = ({ <div className="timer-container"> <div className="timer-control"> <Button - variant="outline-subtle" - size="small" + variant="solid-subtle" + size="medium" rounded disabled={time <= minTime} onClick={() => handleDecrease()} @@ -104,22 +128,24 @@ export const Timer: React.FunctionComponent<Props> = ({ <div className="timer-display">{formatDisplayTime(time)}</div> <Button - variant="outline-subtle" - size="small" + variant="solid-subtle" + size="medium" rounded disabled={time >= maxTime} onClick={() => handleAdd()} > <IconPlus /> </Button> + <Button + variant="solid-subtle" + size="medium" + rounded + disabled={time >= bigStepMaxTime} + onClick={() => handleBigAdd()} + > + +5m + </Button> </div> - <button - className="button button-primary button-small" - type="button" - onClick={() => handleSetTime()} - > - Set - </button> </div> </DropdownMenu.Content> </DropdownMenu> diff --git a/examples/breakout-rooms/src/components/WaitingList/WaitingList.css b/examples/breakout-rooms/src/components/WaitingList/WaitingList.css index 1716975ae..c11123f3b 100644 --- a/examples/breakout-rooms/src/components/WaitingList/WaitingList.css +++ b/examples/breakout-rooms/src/components/WaitingList/WaitingList.css @@ -1,10 +1,10 @@ .waiting-list { - border: 1px solid #d4d4d9; - padding: 1em; - border-radius: 0.25em; + border: 1px solid var(--indigo300); + padding: var(--space-small); + border-radius: var(--border-radius-medium); display: flex; flex-direction: column; - gap: 1em; + gap: var(--space-small); } .waiting-list .waiting-list-title { diff --git a/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx b/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx index f62082f47..dfcdddfe2 100644 --- a/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx +++ b/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx @@ -18,7 +18,8 @@ export const WaitingList: React.FC<Props> = ({ return ( <section className="waiting-list"> <h5 className="waiting-list-title"> - {unassignedUsers.length} user(s) not in rooms + {unassignedUsers.length} {unassignedUsers.length > 1 ? "users" : "user"}{" "} + not in rooms </h5> <Button variant="solid-subtle" diff --git a/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.css b/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.css index 371444a8c..8b0c369a2 100644 --- a/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.css +++ b/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.css @@ -8,8 +8,8 @@ } .waiting-room-message { - background: #f5f5f3; - padding: 1em; + background: var(--indigo50); + padding: var(--space-small); border-radius: 0.5em; } diff --git a/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.tsx b/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.tsx index 28c40dad2..54f71a283 100644 --- a/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.tsx +++ b/examples/breakout-rooms/src/components/WaitingRoom/WaitingRoom.tsx @@ -21,7 +21,7 @@ export const WaitingRoom: React.FC = () => { <main className="waiting-room-container"> <div className="waiting-room-message"> <h1>You facilitator is preparing the session</h1> - <Button variant="ghost-prominent" size="small" onClick={handleNudge}> + <Button variant="ghost-prominent" size="medium" onClick={handleNudge}> <Button.IconSlot> <IconHandPointing /> </Button.IconSlot> diff --git a/examples/breakout-rooms/src/hooks.tsx b/examples/breakout-rooms/src/hooks.tsx index 9f523a81f..1c9fe1afd 100644 --- a/examples/breakout-rooms/src/hooks.tsx +++ b/examples/breakout-rooms/src/hooks.tsx @@ -112,7 +112,7 @@ export const useBreakout = () => { if (r.id === room.id) { const participant: Participant = { ...user, - state: "Waiting room", + state: "In room", }; const participants = [...r.participants, participant].sort((a, b) => a.name.localeCompare(b.name), @@ -394,7 +394,7 @@ export const useBreakout = () => { await session.end(); room.participants.map((participant) => - updateParticipant(room, participant, { state: "Waiting room" }), + updateParticipant(room, participant, { state: "In room" }), ); }); diff --git a/examples/breakout-rooms/src/types.ts b/examples/breakout-rooms/src/types.ts index 506f29d67..d870d55f1 100644 --- a/examples/breakout-rooms/src/types.ts +++ b/examples/breakout-rooms/src/types.ts @@ -2,10 +2,7 @@ import { Item, OnlineUserInfo, UserInfo } from "@mirohq/websdk-types"; export type BreakoutState = "idle" | "started" | "ended"; -export type ParticipantState = - | "Waiting room" - | "In Session" - | "Invitation Pending"; +export type ParticipantState = "In room" | "In session" | "Invitation pending"; export type Participant = OnlineUserInfo & { state: ParticipantState; diff --git a/examples/breakout-rooms/src/utils.ts b/examples/breakout-rooms/src/utils.ts index d5fb77ca0..c2da64936 100644 --- a/examples/breakout-rooms/src/utils.ts +++ b/examples/breakout-rooms/src/utils.ts @@ -34,7 +34,7 @@ export const formatTime = ( value: number, unit: TimeUnit = "milliseconds", ): string => { - const formatter = new Intl.RelativeTimeFormat("en", { numeric: "auto" }); + const formatter = new Intl.NumberFormat("en"); let formattedValue = value; let formattedUnit = unit; @@ -45,14 +45,15 @@ export const formatTime = ( formattedValue = seconds / 60; formattedUnit = "minutes"; } - if (formattedValue >= 60) { - formattedValue = formattedValue / 60; - formattedUnit = "hours"; - } - if (formattedValue >= 24) { - formattedValue = formattedValue / 24; - formattedUnit = "days"; - } + // Commented for possible future use + // if (formattedValue >= 60) { + // formattedValue = formattedValue / 60; + // formattedUnit = "hours"; + // } + // if (formattedValue >= 24) { + // formattedValue = formattedValue / 24; + // formattedUnit = "days"; + // } return formatter.format(formattedValue, formattedUnit); };