From bcf48fdd5dd99c15ed43ffb3f435ed5e1c2a4e2e Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Tue, 14 Nov 2023 15:15:35 +0000 Subject: [PATCH 1/8] Modified the example view to show either the floating camera client or the avatar one Updated examples and content for the new structure Added basic example with avatar for mmlCollisionStart TODO: Refactor ExampleClient and ExampleAvatarClient to get rid of duplicate code --- .../ExampleAvatarClient.tsx | 28 +++++-- .../AnimatedExampleView/ExampleClient.tsx | 6 +- .../ExampleView/DocsExampleView.tsx | 73 +++++++++++++------ .../ExampleView/ExamplePageExampleView.tsx | 33 +++++++-- .../ExamplePageExampleViewDynamic.tsx | 23 ++---- .../blogPosts/introducing-mml/body.mdx | 3 +- src/content/docs/connectionEvent/index.ts | 8 +- src/content/docs/mmlClickEvent/index.ts | 4 +- .../docs/mmlCollisionStartEvent/index.ts | 14 +++- .../docs/mmlCollisionStartEvent/primary.mml | 15 ++++ .../reference/elements/[reference-id].tsx | 7 +- .../docs/reference/events/[event-id].tsx | 7 +- src/pages/examples.tsx | 3 +- types/docs-reference.ts | 10 ++- types/example.ts | 3 + 15 files changed, 166 insertions(+), 71 deletions(-) create mode 100644 src/content/docs/mmlCollisionStartEvent/primary.mml diff --git a/src/components/AnimatedExampleView/ExampleAvatarClient.tsx b/src/components/AnimatedExampleView/ExampleAvatarClient.tsx index ea3611d..288c2ce 100644 --- a/src/components/AnimatedExampleView/ExampleAvatarClient.tsx +++ b/src/components/AnimatedExampleView/ExampleAvatarClient.tsx @@ -1,6 +1,6 @@ import { EditableNetworkedDOM, NetworkedDOM } from "@mml-io/networked-dom-document"; import * as React from "react"; -import { useEffect, useState } from "react"; +import { CSSProperties, useEffect, useState } from "react"; import { Euler, Vector3 } from "three"; import { getIframeTargetWindow } from "@/src/util/iframe-target"; @@ -8,8 +8,13 @@ import { getIframeTargetWindow } from "@/src/util/iframe-target"; import { LocalAvatarClient } from "./LocalAvatar/LocalAvatarClient"; import { LocalAvatarServer } from "./LocalAvatar/LocalAvatarServer"; -function Container(props: { refProp: React.Ref }) { - return
; +function Container(props: { refProp: React.Ref; clientHeight?: number }) { + return ( +
+ ); } export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props: { @@ -18,6 +23,10 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props clientId: number; position: { x: number; y: number; z: number }; rotation: { x: number; y: number; z: number }; + children?: React.ReactNode; + containerStyle?: CSSProperties; + clientsNumber?: number; + parentHeight?: number; }) { const [client, setClient] = useState(null); const elementRef = React.useRef(null); @@ -50,6 +59,7 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props useEffect(() => { if (elementRef.current && client) { + client.element.setAttribute("style", "height: 100%"); elementRef.current.appendChild(client.element); } }, [elementRef.current, client]); @@ -58,14 +68,20 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props return null; } + const { children, clientsNumber = 1, parentHeight = 368 } = props; + + // 368 is the client height we're using in the docs, we use it as a default value here + const clientHeight = Math.floor(parentHeight / clientsNumber); + return ( <> -
+
+ {children} - Client {props.clientId + 1} + Client
- + ); }); diff --git a/src/components/AnimatedExampleView/ExampleClient.tsx b/src/components/AnimatedExampleView/ExampleClient.tsx index 0f6934a..b86eab9 100644 --- a/src/components/AnimatedExampleView/ExampleClient.tsx +++ b/src/components/AnimatedExampleView/ExampleClient.tsx @@ -15,6 +15,7 @@ export const ExampleClient = React.memo(function ExampleClient(props: { clientId: number; children?: React.ReactNode; clientsNumber?: number; + parentHeight?: number; }) { const [clientState, setClientState] = useState<{ client: MMLWebRunnerClient; @@ -62,9 +63,10 @@ export const ExampleClient = React.memo(function ExampleClient(props: { clientState?.scene.fitContainer(); }, 1); - const { children, clientsNumber = 1 } = props; + // 368 is the client height we're using in the docs, we use it as a default value here + const { children, clientsNumber = 1, parentHeight = 368 } = props; - const clientHeight = Math.floor(368 / clientsNumber); + const clientHeight = Math.floor(parentHeight / clientsNumber); return ( <> diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index 2c5d572..c1fc63b 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -3,12 +3,15 @@ import { EditableNetworkedDOM } from "@mml-io/networked-dom-document"; import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { ExampleAvatarClient } from "@/src/components/AnimatedExampleView/ExampleAvatarClient"; import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; +import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; +import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; -function createDocumentCode(code: string, lightOn: boolean): string { +function createDocumentCode(code: string, lightOn?: boolean): string { return `${ lightOn && '' @@ -17,8 +20,8 @@ function createDocumentCode(code: string, lightOn: boolean): string { export type DocsExampleViewProps = { code: string; - initialClientCount?: number; - baseScene: boolean; + initialClients: ClientType[]; + baseScene?: boolean; description: string; showClientsControls?: boolean; }; @@ -28,9 +31,9 @@ export function DocsExampleView(props: DocsExampleViewProps) { const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const [clients, setClients] = useState([ - ...Array(props.initialClientCount ?? 1).keys(), - ]); + const [clients, setClients] = useState(props.initialClients); + + const server = useRef(new LocalAvatarServer()); const { baseScene } = props; @@ -56,8 +59,8 @@ export function DocsExampleView(props: DocsExampleViewProps) { setCode(props.code); }, []); - const addNewClient = () => { - setClients((oldClients) => [...oldClients, oldClients.length]); + const addNewClient = (clientType: ClientType) => { + setClients((oldClients) => [...oldClients, clientType]); }; const removeClient = () => { @@ -90,27 +93,51 @@ export function DocsExampleView(props: DocsExampleViewProps) {
-
+
{networkedDOMDocument && ( <> - {clients.map((clientId, index) => { + {clients.map((clientType, index) => { const isLast = index === clients.length - 1 && index !== 0; - return ( - - {props.showClientsControls && (isLast || clientId === 0) && ( + + const children = + props.showClientsControls && (isLast || index === 0) ? ( + <> - )} - + {!isLast && ( + + )} + + ) : null; + + return clientType === CLIENT_TYPES.FLOATING ? ( + + ) : ( + ); })} diff --git a/src/components/ExampleView/ExamplePageExampleView.tsx b/src/components/ExampleView/ExamplePageExampleView.tsx index 548d0a1..5373019 100644 --- a/src/components/ExampleView/ExamplePageExampleView.tsx +++ b/src/components/ExampleView/ExamplePageExampleView.tsx @@ -1,10 +1,13 @@ import { EditableNetworkedDOM } from "@mml-io/networked-dom-document"; import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { ExampleAvatarClient } from "@/src/components/AnimatedExampleView/ExampleAvatarClient"; import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; +import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; +import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; function createDocumentCode(code: string, lightOn: boolean): string { return `${ @@ -15,7 +18,7 @@ function createDocumentCode(code: string, lightOn: boolean): string { export function ExamplePageExampleView(props: { code: string; - initialClientCount?: number; + initialClients?: ClientType[]; baseScene: boolean; description: string; }) { @@ -23,8 +26,8 @@ export function ExamplePageExampleView(props: { const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const clients = [...Array(props.initialClientCount || 1).keys()]; - const { baseScene } = props; + const { baseScene, initialClients = [CLIENT_TYPES.FLOATING] } = props; + const server = useRef(new LocalAvatarServer()); useEffect(() => { const document = new EditableNetworkedDOM( @@ -81,9 +84,25 @@ export function ExamplePageExampleView(props: {
{networkedDOMDocument && ( <> - {clients.map((clientId) => { - return ( - + {initialClients.map((clientType, index) => { + return clientType === CLIENT_TYPES.FLOATING ? ( + + ) : ( + ); })} diff --git a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx index 99b243f..33eae7e 100644 --- a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx +++ b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx @@ -1,10 +1,13 @@ import dynamic from "next/dynamic"; +import { ClientType } from "@/types/docs-reference"; + type ExampleViewProps = { code: string; + initialClients: ClientType[]; baseScene?: boolean; - initialClientCount?: number; - description?: string; + description: string; + showClientsControls?: boolean; }; const ExamplePageExampleViewStatic = dynamic>( @@ -15,18 +18,6 @@ const ExamplePageExampleViewStatic = dynamic>( { ssr: false }, ); -export default function ExamplePageExampleView({ - code, - description, - baseScene, - initialClientCount, -}: ExampleViewProps) { - return ( - - ); +export default function ExamplePageExampleView(props: ExampleViewProps) { + return ; } diff --git a/src/content/blogPosts/introducing-mml/body.mdx b/src/content/blogPosts/introducing-mml/body.mdx index 714109c..40d6877 100644 --- a/src/content/blogPosts/introducing-mml/body.mdx +++ b/src/content/blogPosts/introducing-mml/body.mdx @@ -1,5 +1,6 @@ import * as React from "react"; import DocsExampleView from "@/src/components/ExampleView/DocsExampleViewDynamic"; +import { CLIENT_TYPES } from "@/types/docs-reference"; # Introducing MML: An Open Source Metaverse Markup Language for Multi-User Interactive 3D Experiences _[Marcus Longmuir](https://twitter.com/marcuslongmuir) - 2023-06-15_ @@ -54,7 +55,7 @@ demonstrating one document seen by multiple clients complete with live editing. some attributes:
- + diff --git a/src/content/docs/connectionEvent/index.ts b/src/content/docs/connectionEvent/index.ts index 1cd2746..1655b13 100644 --- a/src/content/docs/connectionEvent/index.ts +++ b/src/content/docs/connectionEvent/index.ts @@ -1,4 +1,4 @@ -import { DocsExamples } from "@/types/docs-reference"; +import { CLIENT_TYPES, DocsExamples } from "@/types/docs-reference"; import disconnected from "./disconnected.mml"; import primary from "./primary.mml"; @@ -9,21 +9,21 @@ export const examples: DocsExamples = { title: "Basic connection event", description: "A label that shows the client ids that have connected.", code: primary, - clientsNumber: 2, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], showClientsControls: true, }, test: { title: "Basic disconnection event", description: "A label that shows the client ids that have disconnected.", code: disconnected, - clientsNumber: 2, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], showClientsControls: true, }, visibleTo: { title: "Visible to event", description: "A label that shows only the client id of one specific client", code: visibleTo, - clientsNumber: 2, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], showClientsControls: true, }, }; diff --git a/src/content/docs/mmlClickEvent/index.ts b/src/content/docs/mmlClickEvent/index.ts index 700522d..61cd993 100644 --- a/src/content/docs/mmlClickEvent/index.ts +++ b/src/content/docs/mmlClickEvent/index.ts @@ -1,4 +1,4 @@ -import { DocsExamples } from "@/types/docs-reference"; +import { CLIENT_TYPES, DocsExamples } from "@/types/docs-reference"; import clickedBy from "./clicked-by.mml"; import position from "./position.mml"; @@ -14,7 +14,7 @@ export const examples: DocsExamples = { title: "Clicked by", description: "This example shows which client clicked the cube.", code: clickedBy, - clientsNumber: 2, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], showClientsControls: true, }, position: { diff --git a/src/content/docs/mmlCollisionStartEvent/index.ts b/src/content/docs/mmlCollisionStartEvent/index.ts index c4db6f1..91422e4 100644 --- a/src/content/docs/mmlCollisionStartEvent/index.ts +++ b/src/content/docs/mmlCollisionStartEvent/index.ts @@ -1,3 +1,13 @@ -import { DocsExamples } from "@/types/docs-reference"; +import primary from "@/src/content/docs/mmlCollisionStartEvent/primary.mml"; +import { CLIENT_TYPES, DocsExamples } from "@/types/docs-reference"; -export const examples: DocsExamples = {}; +export const examples: DocsExamples = { + primary: { + title: "Collision start event", + description: "The label shows the connection id of the user colliding with the platform.", + code: primary, + clients: [CLIENT_TYPES.AVATAR, CLIENT_TYPES.AVATAR], + showClientsControls: true, + baseSceneOn: false, + }, +}; diff --git a/src/content/docs/mmlCollisionStartEvent/primary.mml b/src/content/docs/mmlCollisionStartEvent/primary.mml new file mode 100644 index 0000000..202d2a1 --- /dev/null +++ b/src/content/docs/mmlCollisionStartEvent/primary.mml @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/pages/docs/reference/elements/[reference-id].tsx b/src/pages/docs/reference/elements/[reference-id].tsx index c737efd..f86f50d 100644 --- a/src/pages/docs/reference/elements/[reference-id].tsx +++ b/src/pages/docs/reference/elements/[reference-id].tsx @@ -12,6 +12,7 @@ import ExampleView from "@/src/components/ExampleView/DocsExampleViewDynamic"; import { MarkDown } from "@/src/config/mdx"; import * as docsExamples from "@/src/content/docs"; import { getPageTitle } from "@/src/util"; +import { CLIENT_TYPES } from "@/types/docs-reference"; // This function gets called at build time to generate all the files export function getStaticPaths() { @@ -86,13 +87,13 @@ const DocsPage = ({ referenceId }: { referenceId: string }) => { Try it )} @@ -141,7 +142,7 @@ const DocsPage = ({ referenceId }: { referenceId: string }) => { description={example.title} baseScene={example.baseSceneOn !== undefined ? example.baseSceneOn : true} code={example.code} - initialClientCount={1} + initialClients={example.clients ?? [CLIENT_TYPES.FLOATING]} />
); diff --git a/src/pages/docs/reference/events/[event-id].tsx b/src/pages/docs/reference/events/[event-id].tsx index 844bf7b..2550e7b 100644 --- a/src/pages/docs/reference/events/[event-id].tsx +++ b/src/pages/docs/reference/events/[event-id].tsx @@ -15,6 +15,7 @@ import { MarkDown } from "@/src/config/mdx"; import * as docsExamples from "@/src/content/docs"; import { getPageTitle } from "@/src/util"; import { eventClasses } from "@/src/util/event-classes"; +import { CLIENT_TYPES } from "@/types/docs-reference"; // This function gets called at build time to generate all the files export function getStaticPaths() { @@ -115,13 +116,13 @@ const DocsPage = ({ eventId }: { eventId: string }) => { Try it @@ -186,7 +187,7 @@ const DocsPage = ({ eventId }: { eventId: string }) => { description={example.title} baseScene={example.baseSceneOn !== undefined ? example.baseSceneOn : true} code={example.code} - initialClientCount={example?.clientsNumber ?? 1} + initialClients={example.clients ?? [CLIENT_TYPES.FLOATING]} showClientsControls={example?.showClientsControls} />
diff --git a/src/pages/examples.tsx b/src/pages/examples.tsx index e24d600..2d8bd9f 100644 --- a/src/pages/examples.tsx +++ b/src/pages/examples.tsx @@ -8,6 +8,7 @@ import { ChangeEvent, useEffect } from "react"; import ExampleView from "@/src/components/ExampleView/ExamplePageExampleViewDynamic"; import { examples } from "@/src/content/examples"; import { getPageTitle } from "@/src/util"; +import { CLIENT_TYPES } from "@/types/docs-reference"; import { Example, ExamplesByName } from "@/types/example"; const ExamplesPage = () => { @@ -109,7 +110,7 @@ const ExamplesPage = () => {
Date: Wed, 15 Nov 2023 11:53:31 +0000 Subject: [PATCH 2/8] Fixed avatar clients not refreshing correctly --- .../AnimatedExampleView/LocalAvatar/LocalAvatarServer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer.ts b/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer.ts index 3857313..66b1f79 100644 --- a/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer.ts +++ b/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer.ts @@ -23,5 +23,9 @@ export class LocalAvatarServer { removeClient(clientId: number) { this.callbacks.delete(clientId); + + this.callbacks.forEach((callback) => { + callback(clientId, null); + }); } } From 2ae3aa6de1e9770534f02f12806cb4fb0cccd19b Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Thu, 16 Nov 2023 08:08:43 +0000 Subject: [PATCH 3/8] Created new grid system to show up to 4 clients Limited the number of clients to 4 Moved client creation to the top bar of Example View Add a button to close individual clients Fixed client references/id to close the right client when clicking on it Changed base scene to be set on example client with three.js instead of the code passed down to all clients Create new components for some of the sections Some renaming Added FontAwesome icons --- package-lock.json | 98 +++++++++++ package.json | 16 +- .../AnimatedExampleView.tsx | 8 +- .../ExampleView/DocsExampleView.tsx | 155 ++++++++---------- .../ExampleView/DocsExampleViewDynamic.tsx | 6 +- .../ExampleAvatarClient.tsx | 36 +--- .../ExampleView/ExampleClientView.tsx | 14 ++ .../ExampleView/ExampleClientsSection.tsx | 83 ++++++++++ .../ExampleFloatingClient.tsx} | 39 ++--- .../ExampleView/ExamplePageExampleView.tsx | 44 ++--- 10 files changed, 318 insertions(+), 181 deletions(-) rename src/components/{AnimatedExampleView => ExampleView}/ExampleAvatarClient.tsx (58%) create mode 100644 src/components/ExampleView/ExampleClientView.tsx create mode 100644 src/components/ExampleView/ExampleClientsSection.tsx rename src/components/{AnimatedExampleView/ExampleClient.tsx => ExampleView/ExampleFloatingClient.tsx} (62%) diff --git a/package-lock.json b/package-lock.json index e900c91..c7d1f18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,10 @@ "": { "name": "mml-website", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@mdx-js/loader": "^2.3.0", "@mdx-js/react": "^2.3.0", "@mml-io/3d-web-client-core": "0.9.0", @@ -817,6 +821,63 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", + "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", + "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", + "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", + "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", @@ -30318,6 +30379,43 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==" }, + "@fortawesome/fontawesome-common-types": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", + "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz", + "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.4.2" + } + }, + "@fortawesome/free-regular-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.4.2.tgz", + "integrity": "sha512-0+sIUWnkgTVVXVAPQmW4vxb9ZTHv0WstOa3rBx9iPxrrrDH6bNLsDYuwXF9b6fGm+iR7DKQvQshUH/FJm3ed9Q==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.4.2" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz", + "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.4.2" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "requires": { + "prop-types": "^15.8.1" + } + }, "@humanwhocodes/config-array": { "version": "0.11.11", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", diff --git a/package.json b/package.json index 521a82a..15201bd 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,10 @@ } }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.4.2", + "@fortawesome/free-regular-svg-icons": "^6.4.2", + "@fortawesome/free-solid-svg-icons": "^6.4.2", + "@fortawesome/react-fontawesome": "^0.2.0", "@mdx-js/loader": "^2.3.0", "@mdx-js/react": "^2.3.0", "@mml-io/3d-web-client-core": "0.9.0", @@ -48,16 +52,17 @@ "@monaco-editor/react": "^4.5.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", + "@types/codemirror": "5.60.10", + "@types/js-beautify": "^1.14.1", "@types/node": "^18.16.3", "@types/react": "^18.2.0", - "@types/three": "^0.153.0", "@types/react-syntax-highlighter": "^15.5.8", - "@types/codemirror": "5.60.10", - "@types/js-beautify": "^1.14.1", + "@types/three": "^0.153.0", "@typescript-eslint/eslint-plugin": "5.59.2", "@typescript-eslint/parser": "5.59.2", "autoprefixer": "^10.4.14", "canvas": "^2.11.2", + "cross-env": "7.0.3", "eslint-config-prettier": "8.8.0", "eslint-config-standard": "17.0.0", "eslint-import-resolver-typescript": "3.5.5", @@ -75,7 +80,6 @@ "prettier-plugin-tailwindcss": "^0.2.8", "raw-loader": "^4.0.2", "tailwindcss": "^3.3.2", - "typescript": "5.0.4", - "cross-env": "7.0.3" + "typescript": "5.0.4" } -} \ No newline at end of file +} diff --git a/src/components/AnimatedExampleView/AnimatedExampleView.tsx b/src/components/AnimatedExampleView/AnimatedExampleView.tsx index e8d6ba8..2d583d5 100644 --- a/src/components/AnimatedExampleView/AnimatedExampleView.tsx +++ b/src/components/AnimatedExampleView/AnimatedExampleView.tsx @@ -6,10 +6,10 @@ import * as React from "react"; import { useCallback, useEffect, useRef, useState } from "react"; import { AnimatedEditorContainer, CodemirrorEditor } from "@/src/components/AnimatedEditor"; -import { ExampleAvatarClient } from "@/src/components/AnimatedExampleView/ExampleAvatarClient"; import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; +import { ExampleAvatarClient } from "@/src/components/ExampleView/ExampleAvatarClient"; -import { ExampleClient } from "./ExampleClient"; +import { ExampleFloatingClient } from "../ExampleView/ExampleFloatingClient"; function createDocumentCode(code: string): string { return `${''}${code}`; @@ -103,7 +103,7 @@ export function AnimatedExampleView() { {networkedDOMDocument && ( <>
- +
- +
)} diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index c1fc63b..450265d 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -1,21 +1,20 @@ "use client"; +import { faArrowRotateRight } from "@fortawesome/free-solid-svg-icons/faArrowRotateRight"; +import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { EditableNetworkedDOM } from "@mml-io/networked-dom-document"; import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import { twMerge } from "tailwind-merge"; -import { ExampleAvatarClient } from "@/src/components/AnimatedExampleView/ExampleAvatarClient"; -import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; -import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; +import ExampleClientsSection from "@/src/components/ExampleView/ExampleClientsSection"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; -function createDocumentCode(code: string, lightOn?: boolean): string { - return `${ - lightOn && - '' - }${code}`; +function generateRandomId() { + return `${Math.random().toString(36).substr(2, 9)}_${Date.now()}`; } export type DocsExampleViewProps = { @@ -27,13 +26,18 @@ export type DocsExampleViewProps = { }; export function DocsExampleView(props: DocsExampleViewProps) { + const [clients, setClients] = useState<{ type: ClientType; id: string }[]>( + props.initialClients.map((type) => ({ + type, + id: generateRandomId(), + })), + ); + const [code, setCode] = useState(props.code); const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const [clients, setClients] = useState(props.initialClients); - - const server = useRef(new LocalAvatarServer()); + const [showAddButtons, setShowAddButtons] = useState(false); const { baseScene } = props; @@ -43,7 +47,7 @@ export function DocsExampleView(props: DocsExampleViewProps) { IframeObservableDOMFactory, true, ); - document.load(createDocumentCode(code, baseScene)); + document.load(code); setNetworkedDOMDocument(document); return () => { @@ -52,40 +56,50 @@ export function DocsExampleView(props: DocsExampleViewProps) { }, []); useEffect(() => { - networkedDOMDocument?.load(createDocumentCode(code, baseScene)); - }, [code, baseScene]); + networkedDOMDocument?.load(code); + }, [code]); const handleResetClick = useCallback(() => { setCode(props.code); }, []); + const handlePlusButton = () => { + if (clients.length >= 4) return; + setShowAddButtons((prev) => !prev); + }; + const addNewClient = (clientType: ClientType) => { - setClients((oldClients) => [...oldClients, clientType]); + if (clients.length >= 4) return; + const newClient = { + type: clientType, + id: generateRandomId(), + }; + setClients((oldClients) => [...oldClients, newClient]); + setShowAddButtons(false); }; - const removeClient = () => { - setClients((oldClients) => oldClients.slice(0, oldClients.length - 1)); + const removeClient = (elemId: string) => { + setClients((oldClients) => oldClients.filter(({ id }) => id !== elemId)); }; return ( - <> -
+
+
{props.description} - +
+ + +
-
+
CODE @@ -93,57 +107,30 @@ export function DocsExampleView(props: DocsExampleViewProps) {
-
- {networkedDOMDocument && ( - <> - {clients.map((clientType, index) => { - const isLast = index === clients.length - 1 && index !== 0; - - const children = - props.showClientsControls && (isLast || index === 0) ? ( - <> - - {!isLast && ( - - )} - - ) : null; - - return clientType === CLIENT_TYPES.FLOATING ? ( - - ) : ( - - ); - })} - - )} -
+
- + {showAddButtons && ( +
+ + +
+ )} +
); } diff --git a/src/components/ExampleView/DocsExampleViewDynamic.tsx b/src/components/ExampleView/DocsExampleViewDynamic.tsx index e1d811f..48fc6ca 100644 --- a/src/components/ExampleView/DocsExampleViewDynamic.tsx +++ b/src/components/ExampleView/DocsExampleViewDynamic.tsx @@ -34,5 +34,9 @@ export default function ExampleView(props: DocsExampleViewProps) { return () => observer.disconnect(); }, []); - return
{isVisible && }
; + return ( +
+ {isVisible && } +
+ ); } diff --git a/src/components/AnimatedExampleView/ExampleAvatarClient.tsx b/src/components/ExampleView/ExampleAvatarClient.tsx similarity index 58% rename from src/components/AnimatedExampleView/ExampleAvatarClient.tsx rename to src/components/ExampleView/ExampleAvatarClient.tsx index 288c2ce..5714ecf 100644 --- a/src/components/AnimatedExampleView/ExampleAvatarClient.tsx +++ b/src/components/ExampleView/ExampleAvatarClient.tsx @@ -1,21 +1,13 @@ import { EditableNetworkedDOM, NetworkedDOM } from "@mml-io/networked-dom-document"; import * as React from "react"; -import { CSSProperties, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { Euler, Vector3 } from "three"; +import ExampleClientView from "@/src/components/ExampleView/ExampleClientView"; import { getIframeTargetWindow } from "@/src/util/iframe-target"; -import { LocalAvatarClient } from "./LocalAvatar/LocalAvatarClient"; -import { LocalAvatarServer } from "./LocalAvatar/LocalAvatarServer"; - -function Container(props: { refProp: React.Ref; clientHeight?: number }) { - return ( -
- ); -} +import { LocalAvatarClient } from "../AnimatedExampleView/LocalAvatar/LocalAvatarClient"; +import { LocalAvatarServer } from "../AnimatedExampleView/LocalAvatar/LocalAvatarServer"; export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props: { server: LocalAvatarServer; @@ -24,9 +16,6 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props position: { x: number; y: number; z: number }; rotation: { x: number; y: number; z: number }; children?: React.ReactNode; - containerStyle?: CSSProperties; - clientsNumber?: number; - parentHeight?: number; }) { const [client, setClient] = useState(null); const elementRef = React.useRef(null); @@ -68,20 +57,5 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props return null; } - const { children, clientsNumber = 1, parentHeight = 368 } = props; - - // 368 is the client height we're using in the docs, we use it as a default value here - const clientHeight = Math.floor(parentHeight / clientsNumber); - - return ( - <> -
- {children} - - Client - -
- - - ); + return {props.children}; }); diff --git a/src/components/ExampleView/ExampleClientView.tsx b/src/components/ExampleView/ExampleClientView.tsx new file mode 100644 index 0000000..49d6742 --- /dev/null +++ b/src/components/ExampleView/ExampleClientView.tsx @@ -0,0 +1,14 @@ +import * as React from "react"; + +export default function ExampleClientView(props: { + children?: React.ReactNode; + elementRef: React.Ref; +}) { + const { children, elementRef } = props; + + return ( +
+ {children} +
+ ); +} diff --git a/src/components/ExampleView/ExampleClientsSection.tsx b/src/components/ExampleView/ExampleClientsSection.tsx new file mode 100644 index 0000000..1b2f9ed --- /dev/null +++ b/src/components/ExampleView/ExampleClientsSection.tsx @@ -0,0 +1,83 @@ +import { EditableNetworkedDOM } from "@mml-io/networked-dom-document"; +import * as React from "react"; +import { useRef } from "react"; + +import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; +import { ExampleAvatarClient } from "@/src/components/ExampleView/ExampleAvatarClient"; +import { ExampleFloatingClient } from "@/src/components/ExampleView/ExampleFloatingClient"; +import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; + +type ExampleClientsSectionProps = { + clients: { type: ClientType; id: string }[]; + removeClient?: (index: string) => void; + showClientsControls?: boolean; + networkedDOMDocument: EditableNetworkedDOM | null; + sectionWidth?: string; + baseScene?: boolean; +}; + +export default function ExampleClientsSection({ + clients, + removeClient, + showClientsControls, + networkedDOMDocument, + sectionWidth = "50%", + baseScene, +}: ExampleClientsSectionProps) { + const server = useRef(new LocalAvatarServer()); + + // Determine the number of columns and rows based on the number of clients + const columns = clients.length < 3 ? 1 : 2; // We want one or two clients per row + const rows = Math.min(clients.length, 2); // Calculate the number of rows needed + + const gridStyle = { + display: "grid", + gridTemplateColumns: `repeat(${columns}, 1fr)`, // Create as many columns as needed + gridTemplateRows: `repeat(${rows}, 1fr)`, // Create as many rows as needed + height: "100%", + width: "100%", + }; + + return ( +
+ {networkedDOMDocument && ( +
+ {clients.map(({ type, id }, index) => { + const children = showClientsControls && ( + <> + + + ); + + return type === CLIENT_TYPES.FLOATING ? ( + + ) : ( + + ); + })} +
+ )} +
+ ); +} diff --git a/src/components/AnimatedExampleView/ExampleClient.tsx b/src/components/ExampleView/ExampleFloatingClient.tsx similarity index 62% rename from src/components/AnimatedExampleView/ExampleClient.tsx rename to src/components/ExampleView/ExampleFloatingClient.tsx index b86eab9..ac8fe01 100644 --- a/src/components/AnimatedExampleView/ExampleClient.tsx +++ b/src/components/ExampleView/ExampleFloatingClient.tsx @@ -3,19 +3,16 @@ import { MMLScene } from "mml-web"; import { MMLWebRunnerClient } from "mml-web-runner"; import * as React from "react"; import { useEffect, useState } from "react"; +import * as THREE from "three"; +import ExampleClientView from "@/src/components/ExampleView/ExampleClientView"; import { getIframeTargetWindow } from "@/src/util/iframe-target"; -function Container(props: { refProp: React.Ref; clientHeight: number }) { - return
; -} - -export const ExampleClient = React.memo(function ExampleClient(props: { +export const ExampleFloatingClient = React.memo(function ExampleClient(props: { document: NetworkedDOM | EditableNetworkedDOM; clientId: number; children?: React.ReactNode; - clientsNumber?: number; - parentHeight?: number; + baseScene?: boolean; }) { const [clientState, setClientState] = useState<{ client: MMLWebRunnerClient; @@ -33,6 +30,17 @@ export const ExampleClient = React.memo(function ExampleClient(props: { return; } mmlScene = new MMLScene(); + if (props.baseScene) { + const pointLight = new THREE.PointLight(0xffffff, 1, 100); + pointLight.position.set(10, 10, 10); + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(20, 20), + new THREE.MeshPhongMaterial({ color: 0xffffff }), + ); + plane.rotation.x = -Math.PI / 2; + mmlScene.getThreeScene().add(plane); + mmlScene.getThreeScene().add(pointLight); + } runnerClient = new MMLWebRunnerClient(wrapper.iframeWindow, wrapper.iframeBody, mmlScene); runnerClient.connect(props.document); setClientState({ client: runnerClient, scene: mmlScene }); @@ -63,20 +71,5 @@ export const ExampleClient = React.memo(function ExampleClient(props: { clientState?.scene.fitContainer(); }, 1); - // 368 is the client height we're using in the docs, we use it as a default value here - const { children, clientsNumber = 1, parentHeight = 368 } = props; - - const clientHeight = Math.floor(parentHeight / clientsNumber); - - return ( - <> -
- {children} - - Client - -
- - - ); + return {props.children}; }); diff --git a/src/components/ExampleView/ExamplePageExampleView.tsx b/src/components/ExampleView/ExamplePageExampleView.tsx index 5373019..a0225d1 100644 --- a/src/components/ExampleView/ExamplePageExampleView.tsx +++ b/src/components/ExampleView/ExamplePageExampleView.tsx @@ -3,9 +3,7 @@ import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; import { useCallback, useEffect, useRef, useState } from "react"; -import { ExampleAvatarClient } from "@/src/components/AnimatedExampleView/ExampleAvatarClient"; -import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; -import { LocalAvatarServer } from "@/src/components/AnimatedExampleView/LocalAvatar/LocalAvatarServer"; +import ExampleClientsSection from "@/src/components/ExampleView/ExampleClientsSection"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; @@ -22,12 +20,16 @@ export function ExamplePageExampleView(props: { baseScene: boolean; description: string; }) { + const { baseScene, initialClients = [CLIENT_TYPES.FLOATING] } = props; const [code, setCode] = useState(props.code); const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const { baseScene, initialClients = [CLIENT_TYPES.FLOATING] } = props; - const server = useRef(new LocalAvatarServer()); + + const clients = initialClients.map((type) => ({ + type, + id: `${Math.random().toString(36).substr(2, 9)}_${Date.now()}`, + })); useEffect(() => { const document = new EditableNetworkedDOM( @@ -81,33 +83,11 @@ export function ExamplePageExampleView(props: { setCode={setCode} />
-
- {networkedDOMDocument && ( - <> - {initialClients.map((clientType, index) => { - return clientType === CLIENT_TYPES.FLOATING ? ( - - ) : ( - - ); - })} - - )} -
+
); From 16ed474af7a94749b57b15e1c2d9b0b1b090fc1c Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Thu, 16 Nov 2023 08:26:09 +0000 Subject: [PATCH 4/8] Removed all instances of showClientsControl since they have become unnecessary Refactored m-light examples to avoid conflicts with avatar client --- src/components/ExampleView/DocsExampleView.tsx | 2 -- .../ExampleView/ExampleClientsSection.tsx | 18 +++++++----------- .../ExamplePageExampleViewDynamic.tsx | 1 - src/content/docs/connectionEvent/index.ts | 3 --- src/content/docs/m-light/point.mml | 2 +- src/content/docs/m-light/primary.mml | 2 +- src/content/docs/m-light/spotlight.mml | 2 +- src/content/docs/mmlClickEvent/index.ts | 1 - .../docs/mmlCollisionStartEvent/index.ts | 1 - src/pages/docs/reference/events/[event-id].tsx | 2 -- types/docs-reference.ts | 1 - 11 files changed, 10 insertions(+), 25 deletions(-) diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index 450265d..b13de29 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -22,7 +22,6 @@ export type DocsExampleViewProps = { initialClients: ClientType[]; baseScene?: boolean; description: string; - showClientsControls?: boolean; }; export function DocsExampleView(props: DocsExampleViewProps) { @@ -110,7 +109,6 @@ export function DocsExampleView(props: DocsExampleViewProps) { diff --git a/src/components/ExampleView/ExampleClientsSection.tsx b/src/components/ExampleView/ExampleClientsSection.tsx index 1b2f9ed..33df813 100644 --- a/src/components/ExampleView/ExampleClientsSection.tsx +++ b/src/components/ExampleView/ExampleClientsSection.tsx @@ -10,7 +10,6 @@ import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; type ExampleClientsSectionProps = { clients: { type: ClientType; id: string }[]; removeClient?: (index: string) => void; - showClientsControls?: boolean; networkedDOMDocument: EditableNetworkedDOM | null; sectionWidth?: string; baseScene?: boolean; @@ -19,7 +18,6 @@ type ExampleClientsSectionProps = { export default function ExampleClientsSection({ clients, removeClient, - showClientsControls, networkedDOMDocument, sectionWidth = "50%", baseScene, @@ -45,15 +43,13 @@ export default function ExampleClientsSection({ {networkedDOMDocument && (
{clients.map(({ type, id }, index) => { - const children = showClientsControls && ( - <> - - + const children = ( + ); return type === CLIENT_TYPES.FLOATING ? ( diff --git a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx index 33eae7e..8516c14 100644 --- a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx +++ b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx @@ -7,7 +7,6 @@ type ExampleViewProps = { initialClients: ClientType[]; baseScene?: boolean; description: string; - showClientsControls?: boolean; }; const ExamplePageExampleViewStatic = dynamic>( diff --git a/src/content/docs/connectionEvent/index.ts b/src/content/docs/connectionEvent/index.ts index 1655b13..9cc1f87 100644 --- a/src/content/docs/connectionEvent/index.ts +++ b/src/content/docs/connectionEvent/index.ts @@ -10,20 +10,17 @@ export const examples: DocsExamples = { description: "A label that shows the client ids that have connected.", code: primary, clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], - showClientsControls: true, }, test: { title: "Basic disconnection event", description: "A label that shows the client ids that have disconnected.", code: disconnected, clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], - showClientsControls: true, }, visibleTo: { title: "Visible to event", description: "A label that shows only the client id of one specific client", code: visibleTo, clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], - showClientsControls: true, }, }; diff --git a/src/content/docs/m-light/point.mml b/src/content/docs/m-light/point.mml index 4b81302..9c744df 100644 --- a/src/content/docs/m-light/point.mml +++ b/src/content/docs/m-light/point.mml @@ -10,4 +10,4 @@ - + diff --git a/src/content/docs/m-light/primary.mml b/src/content/docs/m-light/primary.mml index c05e3c7..5d10330 100644 --- a/src/content/docs/m-light/primary.mml +++ b/src/content/docs/m-light/primary.mml @@ -11,4 +11,4 @@ - + diff --git a/src/content/docs/m-light/spotlight.mml b/src/content/docs/m-light/spotlight.mml index 2fa6530..d382966 100644 --- a/src/content/docs/m-light/spotlight.mml +++ b/src/content/docs/m-light/spotlight.mml @@ -13,4 +13,4 @@ - + diff --git a/src/content/docs/mmlClickEvent/index.ts b/src/content/docs/mmlClickEvent/index.ts index 61cd993..661237f 100644 --- a/src/content/docs/mmlClickEvent/index.ts +++ b/src/content/docs/mmlClickEvent/index.ts @@ -15,7 +15,6 @@ export const examples: DocsExamples = { description: "This example shows which client clicked the cube.", code: clickedBy, clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], - showClientsControls: true, }, position: { title: "Position", diff --git a/src/content/docs/mmlCollisionStartEvent/index.ts b/src/content/docs/mmlCollisionStartEvent/index.ts index 91422e4..5cdc890 100644 --- a/src/content/docs/mmlCollisionStartEvent/index.ts +++ b/src/content/docs/mmlCollisionStartEvent/index.ts @@ -7,7 +7,6 @@ export const examples: DocsExamples = { description: "The label shows the connection id of the user colliding with the platform.", code: primary, clients: [CLIENT_TYPES.AVATAR, CLIENT_TYPES.AVATAR], - showClientsControls: true, baseSceneOn: false, }, }; diff --git a/src/pages/docs/reference/events/[event-id].tsx b/src/pages/docs/reference/events/[event-id].tsx index 2550e7b..06f7185 100644 --- a/src/pages/docs/reference/events/[event-id].tsx +++ b/src/pages/docs/reference/events/[event-id].tsx @@ -123,7 +123,6 @@ const DocsPage = ({ eventId }: { eventId: string }) => { } code={primaryExample.code} initialClients={primaryExample.clients ?? [CLIENT_TYPES.FLOATING]} - showClientsControls={primaryExample?.showClientsControls} /> )} @@ -188,7 +187,6 @@ const DocsPage = ({ eventId }: { eventId: string }) => { baseScene={example.baseSceneOn !== undefined ? example.baseSceneOn : true} code={example.code} initialClients={example.clients ?? [CLIENT_TYPES.FLOATING]} - showClientsControls={example?.showClientsControls} />
); diff --git a/types/docs-reference.ts b/types/docs-reference.ts index 7d7878c..9caec1a 100644 --- a/types/docs-reference.ts +++ b/types/docs-reference.ts @@ -12,7 +12,6 @@ export type DocsReference = { code: string; baseSceneOn?: boolean; clients?: ClientType[]; - showClientsControls?: boolean; }; export type DocsExamples = { From bbc96265f6a1c09fbe7daa95ac9ed505d6f0cc7c Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Thu, 16 Nov 2023 13:45:24 +0000 Subject: [PATCH 5/8] Changed assigned id to be a number Made sure ids can't be reassigned Made avatar spawn in different places --- src/components/ExampleView/DocsExampleView.tsx | 13 +++++++------ .../ExampleView/ExampleClientsSection.tsx | 16 +++++++++++----- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index b13de29..2924220 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -13,9 +13,10 @@ import ExampleClientsSection from "@/src/components/ExampleView/ExampleClientsSe import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; -function generateRandomId() { - return `${Math.random().toString(36).substr(2, 9)}_${Date.now()}`; +function getNextClientId() { + return ++getNextClientId.id; } +getNextClientId.id = 0; export type DocsExampleViewProps = { code: string; @@ -25,10 +26,10 @@ export type DocsExampleViewProps = { }; export function DocsExampleView(props: DocsExampleViewProps) { - const [clients, setClients] = useState<{ type: ClientType; id: string }[]>( + const [clients, setClients] = useState<{ type: ClientType; id: number }[]>(() => props.initialClients.map((type) => ({ type, - id: generateRandomId(), + id: getNextClientId(), })), ); @@ -71,13 +72,13 @@ export function DocsExampleView(props: DocsExampleViewProps) { if (clients.length >= 4) return; const newClient = { type: clientType, - id: generateRandomId(), + id: getNextClientId(), }; setClients((oldClients) => [...oldClients, newClient]); setShowAddButtons(false); }; - const removeClient = (elemId: string) => { + const removeClient = (elemId: number) => { setClients((oldClients) => oldClients.filter(({ id }) => id !== elemId)); }; diff --git a/src/components/ExampleView/ExampleClientsSection.tsx b/src/components/ExampleView/ExampleClientsSection.tsx index 33df813..32d2d98 100644 --- a/src/components/ExampleView/ExampleClientsSection.tsx +++ b/src/components/ExampleView/ExampleClientsSection.tsx @@ -8,8 +8,8 @@ import { ExampleFloatingClient } from "@/src/components/ExampleView/ExampleFloat import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; type ExampleClientsSectionProps = { - clients: { type: ClientType; id: string }[]; - removeClient?: (index: string) => void; + clients: { type: ClientType; id: number }[]; + removeClient?: (index: number) => void; networkedDOMDocument: EditableNetworkedDOM | null; sectionWidth?: string; baseScene?: boolean; @@ -52,9 +52,15 @@ export default function ExampleClientsSection({ ); + const avatarPositionForIndex = { + x: [0, 2].includes(index) ? -1 : 1, + y: 0.5, + z: [0, 1].includes(index) ? 5 : 7, + }; + return type === CLIENT_TYPES.FLOATING ? ( ) : ( From 9a44a7da3f73f635672582db74b8303f569f002d Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Thu, 16 Nov 2023 14:01:36 +0000 Subject: [PATCH 6/8] Create utility for sequential ID generation Refactored code to use the aforementioned utility Fixed errors happening on ExamplePageExampleView.tsx --- src/components/ExampleView/DocsExampleView.tsx | 6 ++---- .../ExampleView/ExamplePageExampleView.tsx | 17 +++++++---------- src/util/clients-utils.ts | 6 ++++++ 3 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 src/util/clients-utils.ts diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index 2924220..1e1cc28 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -11,12 +11,10 @@ import { twMerge } from "tailwind-merge"; import ExampleClientsSection from "@/src/components/ExampleView/ExampleClientsSection"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; +import { getClientIdFunctionGenerator } from "@/src/util/clients-utils"; import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; -function getNextClientId() { - return ++getNextClientId.id; -} -getNextClientId.id = 0; +const getNextClientId = getClientIdFunctionGenerator(); export type DocsExampleViewProps = { code: string; diff --git a/src/components/ExampleView/ExamplePageExampleView.tsx b/src/components/ExampleView/ExamplePageExampleView.tsx index a0225d1..368ab08 100644 --- a/src/components/ExampleView/ExamplePageExampleView.tsx +++ b/src/components/ExampleView/ExamplePageExampleView.tsx @@ -1,18 +1,14 @@ import { EditableNetworkedDOM } from "@mml-io/networked-dom-document"; import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import ExampleClientsSection from "@/src/components/ExampleView/ExampleClientsSection"; import HTMLEditor from "@/src/components/ExampleView/HTMLEditor"; +import { getClientIdFunctionGenerator } from "@/src/util/clients-utils"; import { CLIENT_TYPES, ClientType } from "@/types/docs-reference"; -function createDocumentCode(code: string, lightOn: boolean): string { - return `${ - lightOn && - '' - }${code}`; -} +const getNextClientId = getClientIdFunctionGenerator(); export function ExamplePageExampleView(props: { code: string; @@ -28,7 +24,7 @@ export function ExamplePageExampleView(props: { const clients = initialClients.map((type) => ({ type, - id: `${Math.random().toString(36).substr(2, 9)}_${Date.now()}`, + id: getNextClientId(), })); useEffect(() => { @@ -37,7 +33,7 @@ export function ExamplePageExampleView(props: { IframeObservableDOMFactory, true, ); - document.load(createDocumentCode(code, baseScene)); + document.load(code); setNetworkedDOMDocument(document); return () => { @@ -46,7 +42,7 @@ export function ExamplePageExampleView(props: { }, []); useEffect(() => { - networkedDOMDocument?.load(createDocumentCode(code, baseScene)); + networkedDOMDocument?.load(code); }, [code, baseScene]); const handleResetClick = useCallback(() => { @@ -87,6 +83,7 @@ export function ExamplePageExampleView(props: { networkedDOMDocument={networkedDOMDocument} clients={clients} sectionWidth="40%" + baseScene={props.baseScene} />
diff --git a/src/util/clients-utils.ts b/src/util/clients-utils.ts new file mode 100644 index 0000000..e297f00 --- /dev/null +++ b/src/util/clients-utils.ts @@ -0,0 +1,6 @@ +export function getClientIdFunctionGenerator() { + let clientId = 0; + return () => { + return clientId++; + }; +} From 5f4220fc736c72d94ad84a22c7bbcb5efd524bd5 Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Thu, 16 Nov 2023 14:49:37 +0000 Subject: [PATCH 7/8] Hide button on example page clients --- src/components/ExampleView/ExampleClientsSection.tsx | 4 +++- src/components/ExampleView/ExamplePageExampleView.tsx | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ExampleView/ExampleClientsSection.tsx b/src/components/ExampleView/ExampleClientsSection.tsx index 32d2d98..a4e2611 100644 --- a/src/components/ExampleView/ExampleClientsSection.tsx +++ b/src/components/ExampleView/ExampleClientsSection.tsx @@ -13,6 +13,7 @@ type ExampleClientsSectionProps = { networkedDOMDocument: EditableNetworkedDOM | null; sectionWidth?: string; baseScene?: boolean; + hideButtons?: boolean; }; export default function ExampleClientsSection({ @@ -21,6 +22,7 @@ export default function ExampleClientsSection({ networkedDOMDocument, sectionWidth = "50%", baseScene, + hideButtons, }: ExampleClientsSectionProps) { const server = useRef(new LocalAvatarServer()); @@ -43,7 +45,7 @@ export default function ExampleClientsSection({ {networkedDOMDocument && (
{clients.map(({ type, id }, index) => { - const children = ( + const children = hideButtons ? null : (
From f7e7e1c6c137875ee37993a894075745e11a5c20 Mon Sep 17 00:00:00 2001 From: Sacha Morgese Date: Fri, 17 Nov 2023 14:49:34 +0000 Subject: [PATCH 8/8] Fixed missing shadows when base scene enabled Fixed bug happening on events because of missing div --- src/components/ExampleView/ExampleFloatingClient.tsx | 4 +++- src/pages/docs/reference/events/[event-id].tsx | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/ExampleView/ExampleFloatingClient.tsx b/src/components/ExampleView/ExampleFloatingClient.tsx index ac8fe01..2b9946d 100644 --- a/src/components/ExampleView/ExampleFloatingClient.tsx +++ b/src/components/ExampleView/ExampleFloatingClient.tsx @@ -33,10 +33,12 @@ export const ExampleFloatingClient = React.memo(function ExampleClient(props: { if (props.baseScene) { const pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(10, 10, 10); + pointLight.castShadow = true; const plane = new THREE.Mesh( new THREE.PlaneGeometry(20, 20), - new THREE.MeshPhongMaterial({ color: 0xffffff }), + new THREE.MeshStandardMaterial({ color: 0xffffff }), ); + plane.receiveShadow = true; plane.rotation.x = -Math.PI / 2; mmlScene.getThreeScene().add(plane); mmlScene.getThreeScene().add(pointLight); diff --git a/src/pages/docs/reference/events/[event-id].tsx b/src/pages/docs/reference/events/[event-id].tsx index 06f7185..c73a99d 100644 --- a/src/pages/docs/reference/events/[event-id].tsx +++ b/src/pages/docs/reference/events/[event-id].tsx @@ -111,20 +111,19 @@ const DocsPage = ({ eventId }: { eventId: string }) => { )} {primaryExample && ( - <> +

Try it

- +
)}

Properties