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/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); + }); } } diff --git a/src/components/ExampleView/DocsExampleView.tsx b/src/components/ExampleView/DocsExampleView.tsx index 2c5d572..1e1cc28 100644 --- a/src/components/ExampleView/DocsExampleView.tsx +++ b/src/components/ExampleView/DocsExampleView.tsx @@ -1,36 +1,41 @@ "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, useState } from "react"; +import { twMerge } from "tailwind-merge"; -import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; +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 type DocsExampleViewProps = { code: string; - initialClientCount?: number; - baseScene: boolean; + initialClients: ClientType[]; + baseScene?: boolean; description: string; - showClientsControls?: boolean; }; export function DocsExampleView(props: DocsExampleViewProps) { + const [clients, setClients] = useState<{ type: ClientType; id: number }[]>(() => + props.initialClients.map((type) => ({ + type, + id: getNextClientId(), + })), + ); + const [code, setCode] = useState(props.code); const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const [clients, setClients] = useState([ - ...Array(props.initialClientCount ?? 1).keys(), - ]); + const [showAddButtons, setShowAddButtons] = useState(false); const { baseScene } = props; @@ -40,7 +45,7 @@ export function DocsExampleView(props: DocsExampleViewProps) { IframeObservableDOMFactory, true, ); - document.load(createDocumentCode(code, baseScene)); + document.load(code); setNetworkedDOMDocument(document); return () => { @@ -49,40 +54,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 addNewClient = () => { - setClients((oldClients) => [...oldClients, oldClients.length]); + const handlePlusButton = () => { + if (clients.length >= 4) return; + setShowAddButtons((prev) => !prev); }; - const removeClient = () => { - setClients((oldClients) => oldClients.slice(0, oldClients.length - 1)); + const addNewClient = (clientType: ClientType) => { + if (clients.length >= 4) return; + const newClient = { + type: clientType, + id: getNextClientId(), + }; + setClients((oldClients) => [...oldClients, newClient]); + setShowAddButtons(false); + }; + + const removeClient = (elemId: number) => { + setClients((oldClients) => oldClients.filter(({ id }) => id !== elemId)); }; return ( - <> -
+
+
{props.description} - +
+ + +
-
+
CODE @@ -90,33 +105,29 @@ export function DocsExampleView(props: DocsExampleViewProps) {
-
- {networkedDOMDocument && ( - <> - {clients.map((clientId, index) => { - const isLast = index === clients.length - 1 && index !== 0; - return ( - - {props.showClientsControls && (isLast || clientId === 0) && ( - - )} - - ); - })} - - )} -
+
- + {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 70% rename from src/components/AnimatedExampleView/ExampleAvatarClient.tsx rename to src/components/ExampleView/ExampleAvatarClient.tsx index ea3611d..5714ecf 100644 --- a/src/components/AnimatedExampleView/ExampleAvatarClient.tsx +++ b/src/components/ExampleView/ExampleAvatarClient.tsx @@ -3,14 +3,11 @@ import * as React 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 }) { - return
; -} +import { LocalAvatarClient } from "../AnimatedExampleView/LocalAvatar/LocalAvatarClient"; +import { LocalAvatarServer } from "../AnimatedExampleView/LocalAvatar/LocalAvatarServer"; export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props: { server: LocalAvatarServer; @@ -18,6 +15,7 @@ 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; }) { const [client, setClient] = useState(null); const elementRef = React.useRef(null); @@ -50,6 +48,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 +57,5 @@ export const ExampleAvatarClient = React.memo(function ExampleAvatarClient(props return null; } - return ( - <> -
- - Client {props.clientId + 1} - -
- - - ); + 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..a4e2611 --- /dev/null +++ b/src/components/ExampleView/ExampleClientsSection.tsx @@ -0,0 +1,87 @@ +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: number }[]; + removeClient?: (index: number) => void; + networkedDOMDocument: EditableNetworkedDOM | null; + sectionWidth?: string; + baseScene?: boolean; + hideButtons?: boolean; +}; + +export default function ExampleClientsSection({ + clients, + removeClient, + networkedDOMDocument, + sectionWidth = "50%", + baseScene, + hideButtons, +}: 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 = hideButtons ? null : ( + + ); + + const avatarPositionForIndex = { + x: [0, 2].includes(index) ? -1 : 1, + y: 0.5, + z: [0, 1].includes(index) ? 5 : 7, + }; + + return type === CLIENT_TYPES.FLOATING ? ( + + ) : ( + + ); + })} +
+ )} +
+ ); +} diff --git a/src/components/AnimatedExampleView/ExampleClient.tsx b/src/components/ExampleView/ExampleFloatingClient.tsx similarity index 66% rename from src/components/AnimatedExampleView/ExampleClient.tsx rename to src/components/ExampleView/ExampleFloatingClient.tsx index 0f6934a..2b9946d 100644 --- a/src/components/AnimatedExampleView/ExampleClient.tsx +++ b/src/components/ExampleView/ExampleFloatingClient.tsx @@ -3,18 +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; + baseScene?: boolean; }) { const [clientState, setClientState] = useState<{ client: MMLWebRunnerClient; @@ -32,6 +30,19 @@ 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); + pointLight.castShadow = true; + const plane = new THREE.Mesh( + new THREE.PlaneGeometry(20, 20), + new THREE.MeshStandardMaterial({ color: 0xffffff }), + ); + plane.receiveShadow = true; + 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 }); @@ -62,19 +73,5 @@ export const ExampleClient = React.memo(function ExampleClient(props: { clientState?.scene.fitContainer(); }, 1); - const { children, clientsNumber = 1 } = props; - - const clientHeight = Math.floor(368 / clientsNumber); - - return ( - <> -
- {children} - - Client - -
- - - ); + return {props.children}; }); diff --git a/src/components/ExampleView/ExamplePageExampleView.tsx b/src/components/ExampleView/ExamplePageExampleView.tsx index 548d0a1..4b291fb 100644 --- a/src/components/ExampleView/ExamplePageExampleView.tsx +++ b/src/components/ExampleView/ExamplePageExampleView.tsx @@ -3,28 +3,29 @@ import { IframeObservableDOMFactory } from "@mml-io/networked-dom-web-runner"; import * as React from "react"; import { useCallback, useEffect, useState } from "react"; -import { ExampleClient } from "@/src/components/AnimatedExampleView/ExampleClient"; +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; - initialClientCount?: number; + initialClients?: ClientType[]; baseScene: boolean; description: string; }) { + const { baseScene, initialClients = [CLIENT_TYPES.FLOATING] } = props; const [code, setCode] = useState(props.code); const [networkedDOMDocument, setNetworkedDOMDocument] = useState( null, ); - const clients = [...Array(props.initialClientCount || 1).keys()]; - const { baseScene } = props; + + const clients = initialClients.map((type) => ({ + type, + id: getNextClientId(), + })); useEffect(() => { const document = new EditableNetworkedDOM( @@ -32,7 +33,7 @@ export function ExamplePageExampleView(props: { IframeObservableDOMFactory, true, ); - document.load(createDocumentCode(code, baseScene)); + document.load(code); setNetworkedDOMDocument(document); return () => { @@ -41,7 +42,7 @@ export function ExamplePageExampleView(props: { }, []); useEffect(() => { - networkedDOMDocument?.load(createDocumentCode(code, baseScene)); + networkedDOMDocument?.load(code); }, [code, baseScene]); const handleResetClick = useCallback(() => { @@ -78,17 +79,13 @@ export function ExamplePageExampleView(props: { setCode={setCode} />
-
- {networkedDOMDocument && ( - <> - {clients.map((clientId) => { - return ( - - ); - })} - - )} -
+
); diff --git a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx index 99b243f..8516c14 100644 --- a/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx +++ b/src/components/ExampleView/ExamplePageExampleViewDynamic.tsx @@ -1,10 +1,12 @@ 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; }; const ExamplePageExampleViewStatic = dynamic>( @@ -15,18 +17,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..9cc1f87 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,18 @@ export const examples: DocsExamples = { title: "Basic connection event", description: "A label that shows the client ids that have connected.", code: primary, - clientsNumber: 2, - showClientsControls: true, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], }, test: { title: "Basic disconnection event", description: "A label that shows the client ids that have disconnected.", code: disconnected, - clientsNumber: 2, - showClientsControls: true, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], }, visibleTo: { title: "Visible to event", description: "A label that shows only the client id of one specific client", code: visibleTo, - clientsNumber: 2, - showClientsControls: true, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], }, }; 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 700522d..661237f 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,8 +14,7 @@ export const examples: DocsExamples = { title: "Clicked by", description: "This example shows which client clicked the cube.", code: clickedBy, - clientsNumber: 2, - showClientsControls: true, + clients: [CLIENT_TYPES.FLOATING, CLIENT_TYPES.FLOATING], }, position: { title: "Position", diff --git a/src/content/docs/mmlCollisionStartEvent/index.ts b/src/content/docs/mmlCollisionStartEvent/index.ts index c4db6f1..5cdc890 100644 --- a/src/content/docs/mmlCollisionStartEvent/index.ts +++ b/src/content/docs/mmlCollisionStartEvent/index.ts @@ -1,3 +1,12 @@ -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], + 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..c73a99d 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() { @@ -110,21 +111,19 @@ const DocsPage = ({ eventId }: { eventId: string }) => { )} {primaryExample && ( - <> +

Try it

- +
)}

Properties @@ -186,8 +185,7 @@ const DocsPage = ({ eventId }: { eventId: string }) => { description={example.title} baseScene={example.baseSceneOn !== undefined ? example.baseSceneOn : true} code={example.code} - initialClientCount={example?.clientsNumber ?? 1} - showClientsControls={example?.showClientsControls} + initialClients={example.clients ?? [CLIENT_TYPES.FLOATING]} />

); 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 = () => {
{ + return clientId++; + }; +} diff --git a/types/docs-reference.ts b/types/docs-reference.ts index 53ed952..9caec1a 100644 --- a/types/docs-reference.ts +++ b/types/docs-reference.ts @@ -1,10 +1,17 @@ +// Technically not a type but I'm not sure where to put this constant +export const CLIENT_TYPES = { + FLOATING: "floating", + AVATAR: "avatar", +} as const; + +export type ClientType = (typeof CLIENT_TYPES)[keyof typeof CLIENT_TYPES]; + export type DocsReference = { title: string; description: string; code: string; baseSceneOn?: boolean; - clientsNumber?: number; - showClientsControls?: boolean; + clients?: ClientType[]; }; export type DocsExamples = { diff --git a/types/example.ts b/types/example.ts index 66d476f..2682966 100644 --- a/types/example.ts +++ b/types/example.ts @@ -1,9 +1,12 @@ +import { ClientType } from "@/types/docs-reference"; + export type Example = { name: string; image: string; description: string; code: string; baseScene?: boolean; + clients?: ClientType[]; }; export type ExamplesByName = {