diff --git a/packages/client-core/i18n/en/common.json b/packages/client-core/i18n/en/common.json index 106e7e6634d..9422cd0f427 100755 --- a/packages/client-core/i18n/en/common.json +++ b/packages/client-core/i18n/en/common.json @@ -33,6 +33,7 @@ "loadingProjects": "Loading projects...", "loadingEngine": "Loading engine...", "loadingEditor": "Loading editor...", + "loadingStudio": "Loading studio...", "loadingRoutes": "Loading routes...", "loadingRoute": "Loading route...", "loadingAuth": "Loading auth routes...", diff --git a/packages/client-core/i18n/en/editor.json b/packages/client-core/i18n/en/editor.json index d283536c12f..d15db7418f5 100755 --- a/packages/client-core/i18n/en/editor.json +++ b/packages/client-core/i18n/en/editor.json @@ -6,7 +6,7 @@ "error": "Error", "errorMsg": "There was an unknown error.", "generateScreenshot": "Generating Project Screenshot", - "newScene": "Create Scene", + "newScene": "Add Scene", "saving": "Saving Project", "savingError": "Error Saving Project", "savingErrorMsg": "There was an error when saving the project.", @@ -100,6 +100,8 @@ "toolbar": { "lbl-publish": "Publish", "lbl-published": "Published", + "lbl-simple": "Simple", + "lbl-advanced": "Advanced", "gizmo": { "description": "Transform Gizmo", "translate": "[T] Translate", @@ -190,7 +192,7 @@ }, "properties": { "title": "Properties", - "info": "Propeties let you access and edit detailed information about objects in the scene.", + "info": "Properties let you access and edit detailed information about objects in the scene.", "lbl-visible": "Visible", "lbl-preventBake": "Prevent Bake", "lbl-dynamicLoad": "Load Children Dynamically", @@ -206,7 +208,13 @@ }, "envmap": { "name": "Environment Map", - "description": "environment map properties" + "description": "environment map properties", + "lbl-source": "Envmap Source", + "lbl-color": "Envmap Color", + "lbl-bake": "Envmap Bake", + "lbl-textureType": "Texture Type", + "lbl-textureUrl": "Texture URL", + "lbl-intensity": "Envmap Intensity" }, "trigger": { "name": "Trigger", @@ -268,6 +276,7 @@ "media": { "name": "Media", "description": "Audio and video playback", + "path": "Path", "paths": "Source Paths", "playmode": "Play Mode", "playtitle": "Play", @@ -293,6 +302,12 @@ "description": "A mesh is a collection of vertices, edges, and faces that describe the shape of a 3D object.", "geometryEditor": "Geometry Editor", "materialEditor": "Material Editor", + "geometry": { + "name": "Name:", + "count": "Count:", + "itemSize": "Item Size:", + "recalculateNormals": "Recalculate Normals" + }, "material": { "name": "Name", "source": "Source", @@ -341,7 +356,8 @@ "lbl-friction":"Friction", "lbl-restitution":"Restitution", "lbl-collisionLayer":"Collision Layer", - "lbl-collisionMask":"Collision Mask" + "lbl-collisionMask":"Collision Mask", + "lbl-removeMesh": "Remove Mesh" }, "camera": { "name": "Camera", @@ -382,7 +398,8 @@ "title": "Loop Animation", "description": "Looped animation associated with your 3d model, loaded from a GLTF URL or file.", "lbl-loopAnimation": "Loop Animation", - "lbl-timeScale": "Time Scale" + "lbl-timeScale": "Time Scale", + "lbl-animationPack": "Animation Pack (via Mixamo Rig)" }, "variant": { "name": "Variant", @@ -820,7 +837,10 @@ "lbl-useGlobalTransform": "Use Global Transform" }, "triggerVolume": { + "name": "Trigger", "description": "Sets a property on the target object on enter and leave.", + "lbl-addTrigger": "Add Trigger", + "lbl-removeTrigger": "Remove Trigger", "lbl-target": "Object", "lbl-onenter": "On Enter", "lbl-onexit": "On Exit", @@ -958,7 +978,30 @@ "clippingMax": "clip.max", "glyphResolution": "glyph.resolution", "glyphDetail": "glyph.detail", - "gpuAccelerated": "GPU Accelerated" + "gpuAccelerated": "GPU Accelerated" + }, + "textBox": { + "name": "Textbox Component", + "description": "An XRUI text box component", + "lbl-text": "Text", + "lbl-family": "Family", + "lbl-size": "Size", + "lbl-fontStroke": "Font Stroke", + "lbl-fontColor": "Font Color", + "lbl-cornerRadius": "Corner Radius", + "lbl-backgroundColor": "Background Color", + "lbl-opacity": "Opacity", + "lbl-padding": "Padding" + }, + "gallery": { + "name": "Gallery Component", + "description": "XRUI Gallery Component", + "lbl-imageGrid": "ImageGrid Component", + "lbl-thumbnail": "Thumbnail", + "lbl-outfitURL": "Outfit URL", + "lbl-performerURL": "Performer URL", + "lbl-asset": "Asset", + "assets": "Assets" } }, "projects": { @@ -1095,8 +1138,8 @@ "compress": "Compress", "convert": "Convert", "downloadProject": "Download Project", - "uploadAsset": "Upload Asset", - "search-placeholder": "Search files and folders ...", + "uploadAssets": "Upload Assets", + "search-placeholder": "Search folders", "fileProperties": { "name": "Name:", "type": "Type:", @@ -1138,11 +1181,13 @@ "lbl": "Hierarchy", "info": "The scene Hierarchy contains all element currently in your scene (assets, lighting, items from the tool menu, etc).", "lbl-rename": "Rename", + "lbl-renameScene": "Rename Scene", "lbl-duplicate": "Duplicate", "lbl-group": "Group", "lbl-copy": "Copy", "lbl-paste": "Paste", "lbl-delete": "Delete", + "lbl-deleteScene": "Are you sure you want to delete this scene?", "lbl-expandAll": "Expand All", "lbl-collapseAll": "Collapse All", "lbl-explode": "Explode Objects", @@ -1214,7 +1259,8 @@ "saveScene": { "title": "Save", "lbl-thumbnail": "Generate thumbnail & envmap", - "lbl-confirm": "Save Scene" + "lbl-confirm": "Save Scene", + "info-confirm": "Are you sure you want to save the scene?" }, "saveNewScene": { "title": "Save As", diff --git a/packages/client-core/src/common/services/ThemeService.tsx b/packages/client-core/src/common/services/ThemeService.tsx index 3659c580ce9..480f79529c2 100644 --- a/packages/client-core/src/common/services/ThemeService.tsx +++ b/packages/client-core/src/common/services/ThemeService.tsx @@ -86,7 +86,7 @@ const themes = { export const ThemeState = defineState({ name: 'ThemeState', initial: { - theme: 'light' as 'light' | 'dark' | 'custom' + theme: 'dark' as 'light' | 'dark' | 'custom' }, setTheme: (theme: 'light' | 'dark' | 'custom') => { diff --git a/packages/client/src/main.tsx b/packages/client/src/main.tsx index cccc596219a..379a3c0a0ab 100755 --- a/packages/client/src/main.tsx +++ b/packages/client/src/main.tsx @@ -68,17 +68,6 @@ const App = () => { } /> - }> - - - - - } - /> { + const ready = useStudioEditor() + + if (!ready) return + + return ( + }> + + + } /> + + + ) +} + +const EditorProtectedRoutes = () => { + const location = useLocation() + const authState = useHookstate(getMutableState(AuthState)) + const user = authState.user + const isAuthorized = useHookstate(null) + + useEffect(() => { + if (user.scopes.value) { + const hasAccess = userHasAccess('editor:write') + if (!hasAccess) { + RouterState.navigate('/', { redirectUrl: location.pathname }) + isAuthorized.set(false) + } else isAuthorized.set(true) + } + }, [user.scopes]) + + if (!isAuthorized.value) return + + return +} + +export default EditorProtectedRoutes diff --git a/packages/editor/src/components/Editor2Container.css b/packages/editor/src/components/Editor2Container.css new file mode 100755 index 00000000000..e42e4b6d66a --- /dev/null +++ b/packages/editor/src/components/Editor2Container.css @@ -0,0 +1,55 @@ +/** references https://github.com/ticlo/rc-dock/blob/master/dist/rc-dock.css */ + +.dock-top .dock-bar { + border-bottom: 1px solid rgba(0, 0, 0, 0.2); + background: transparent; +} + +.dock-panel { + border: none; +} + +.dock.dock-top { + background: var(--bg-primary); +} + +.dock-nav-list { + justify-content: center; + align-items: center; +} + +.dock-top .dock-bar { + border-bottom: none !important; +} + +.dock-tab { + border-top-left-radius: 5px; + border-top-right-radius: 5px; + background: transparent; + margin: 0; + border-bottom: none !important; +} + +.dock-tab.dock-tab-active { + background: var(--bg-surface-main); +} + +.dock-tab:not(.dock-tab-active):hover { + background: var(--bg-surface-input); +} + +.dock-tab > div { + padding: 0 !important; +} + +.dock-ink-bar { + display: none; +} + +.dock-tab-close-btn{ + display: none; +} + +.dock-bar.drag-initiator{ + padding: 0; +} \ No newline at end of file diff --git a/packages/editor/src/components/Editor2Container.tsx b/packages/editor/src/components/Editor2Container.tsx new file mode 100644 index 00000000000..4494d5e9843 --- /dev/null +++ b/packages/editor/src/components/Editor2Container.tsx @@ -0,0 +1,174 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { assetPath } from '@etherealengine/common/src/schema.type.module' +import { EntityUUID } from '@etherealengine/ecs' +import { getMutableState, useHookstate, useMutableState } from '@etherealengine/hyperflux' +import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' +import { AssetsPanelTab } from '@etherealengine/ui/src/components/editor/panels/Assets' +import { FilesPanelTab } from '@etherealengine/ui/src/components/editor/panels/Files' +import { HierarchyPanelTab } from '@etherealengine/ui/src/components/editor/panels/Hierarchy' +import { MaterialsPanelTab } from '@etherealengine/ui/src/components/editor/panels/Materials' +import { PropertiesPanelTab } from '@etherealengine/ui/src/components/editor/panels/Properties' +import { ScenePanelTab } from '@etherealengine/ui/src/components/editor/panels/Scenes' +import { ViewportPanelTab } from '@etherealengine/ui/src/components/editor/panels/Viewport' +import ErrorDialog from '@etherealengine/ui/src/components/tailwind/ErrorDialog' +import PopupMenu from '@etherealengine/ui/src/primitives/tailwind/PopupMenu' +import { t } from 'i18next' +import { DockLayout, DockMode, LayoutData } from 'rc-dock' +import React, { useEffect, useRef } from 'react' +import { useHotkeys } from 'react-hotkeys-hook' +import Toolbar from '../components/toolbar/Toolbar2' +import { setCurrentEditorScene } from '../functions/sceneFunctions' +import { cmdOrCtrlString } from '../functions/utils' +import { EditorErrorState } from '../services/EditorErrorServices' +import { EditorState } from '../services/EditorServices' +import { SelectionState } from '../services/SelectionServices' +import AssetDropZone from './assets/AssetDropZone' +import { SaveSceneDialog } from './dialogs/SaveSceneDialog2' +import { DndWrapper } from './dnd/DndWrapper' +import DragLayer from './dnd/DragLayer' + +import '@etherealengine/ui/src/fonts/font.css' +import 'rc-dock/dist/rc-dock.css' +import './Editor2Container.css' + +export const DockContainer = ({ children, id = 'editor-dock', dividerAlpha = 0 }) => { + const dockContainerStyles = { + '--dividerAlpha': dividerAlpha + } + + return ( +
+ {children} +
+ ) +} + +const onEditorError = (error) => { + console.error(error) + if (error['aborted']) { + PopoverState.hidePopupover() + return + } + + PopoverState.showPopupover( + + ) +} + +const defaultLayout: LayoutData = { + dockbox: { + mode: 'horizontal' as DockMode, + children: [ + { + mode: 'vertical' as DockMode, + size: 8, + children: [ + { + tabs: [ViewportPanelTab] + }, + { + tabs: [ScenePanelTab, FilesPanelTab, AssetsPanelTab] + } + ] + }, + { + mode: 'vertical' as DockMode, + size: 3, + children: [ + { + tabs: [HierarchyPanelTab, MaterialsPanelTab] + }, + { + tabs: [PropertiesPanelTab] + } + ] + } + ] + } +} + +const EditorContainer = () => { + const { sceneAssetID, sceneName, projectName, scenePath } = useMutableState(EditorState) + const sceneQuery = useFind(assetPath, { query: { assetURL: scenePath.value ?? '' } }).data + + const errorState = useHookstate(getMutableState(EditorErrorState).error) + + const dockPanelRef = useRef(null) + + useHotkeys(`${cmdOrCtrlString}+s`, () => PopoverState.showPopupover()) + + useEffect(() => { + const scene = sceneQuery[0] + if (!scene) return + + projectName.set(scene.projectName) + sceneName.set(scene.assetURL.split('/').pop() ?? null) + sceneAssetID.set(sceneQuery[0].id) + return setCurrentEditorScene(scene.assetURL, scene.id as EntityUUID) + }, [sceneQuery[0]?.assetURL]) + + useEffect(() => { + return () => { + getMutableState(SelectionState).selectedEntities.set([]) + } + }, [scenePath]) + + useEffect(() => { + if (errorState.value) { + onEditorError(errorState.value) + } + }, [errorState]) + + return ( +
+
+ + + +
+ + + + +
+
+
+ +
+ ) +} + +export default EditorContainer diff --git a/packages/editor/src/components/EditorContainer.tsx b/packages/editor/src/components/EditorContainer.tsx index 0e0a936529e..7ea9c34e125 100755 --- a/packages/editor/src/components/EditorContainer.tsx +++ b/packages/editor/src/components/EditorContainer.tsx @@ -442,7 +442,7 @@ const EditorContainer = () => {
diff --git a/packages/editor/src/components/assets/FileBrowser/FileBrowserContentPanel.tsx b/packages/editor/src/components/assets/FileBrowser/FileBrowserContentPanel.tsx index 6147cf14767..975f2943318 100644 --- a/packages/editor/src/components/assets/FileBrowser/FileBrowserContentPanel.tsx +++ b/packages/editor/src/components/assets/FileBrowser/FileBrowserContentPanel.tsx @@ -605,7 +605,7 @@ const FileBrowserContentPanel: React.FC = (props) )} {showUploadAndDownloadButtons && ( { await inputFileWithAddToScene({ directoryPath: selectedDirectory.value }) .then(refreshDirectory) @@ -614,7 +614,7 @@ const FileBrowserContentPanel: React.FC = (props) }) }} icon={AddIcon} - id="uploadAsset" + id="uploadAssets" /> )} diff --git a/packages/editor/src/components/assets/FileBrowser/FileBrowserGrid.tsx b/packages/editor/src/components/assets/FileBrowser/FileBrowserGrid.tsx index 23c382f645c..36c8d8f132d 100755 --- a/packages/editor/src/components/assets/FileBrowser/FileBrowserGrid.tsx +++ b/packages/editor/src/components/assets/FileBrowser/FileBrowserGrid.tsx @@ -184,7 +184,7 @@ export const FileGridItem: React.FC = (props) => {
{scenes.map((scene: AssetType) => ( -
+
onClickExisting(e, scene)}>
}) => { + const { t } = useTranslation() + + return ( + <> + {t('editor:properties.model.transform.compress')} + compressProperties.uastcFlags.set(val)} + /> + + + + + + )} + + ) +} + +export default function ImportSettingsPanel() { + const importSettingsState = useHookstate(getMutableState(ImportSettingsState)) + const compressProperties = useHookstate(getMutableState(ImportSettingsState).imageSettings.get(NO_PROXY)) + + const [defaultImportFolder, setDefaultImportFolder] = useState(importSettingsState.importFolder.value) + const [LODImportFolder, setLODImportFolder] = useState(importSettingsState.LODFolder.value) + const [LODGenEnabled, setLODGenEnabled] = useState(importSettingsState.LODsEnabled.value) + const [selectedLODS, setSelectedLods] = useState( + importSettingsState.selectedLODS.get(NO_PROXY) as LODVariantDescriptor[] + ) + const [currentLOD, setCurrentLOD] = useState( + importSettingsState.selectedLODS[0].get(NO_PROXY) as LODVariantDescriptor + ) + const [currentIndex, setCurrentIndex] = useState(0) + const [KTXEnabled, setKTXEnabled] = useState(importSettingsState.imageCompression.value) + + const presetLabels = ['LOD0', 'LOD1', 'LOD2'] + + useEffect(() => { + handleLODChange() + }, [currentLOD, currentIndex]) + + const handleLODChange = () => { + const newLODS = [...selectedLODS] + newLODS.splice(currentIndex, 1, currentLOD) + setSelectedLods(newLODS) + } + + const handleSaveChanges = () => { + importSettingsState.importFolder.set(defaultImportFolder) + importSettingsState.LODFolder.set(LODImportFolder) + importSettingsState.LODsEnabled.set(LODGenEnabled) + importSettingsState.imageCompression.set(KTXEnabled) + importSettingsState.imageSettings.set(compressProperties.get(NO_PROXY)) + importSettingsState.selectedLODS.set(selectedLODS) + handleCancel() + } + + const handleCancel = () => { + PopoverState.hidePopupover() + } + + return ( + + setDefaultImportFolder(event.target.value)} + label="Default Import Folder" + /> + setLODGenEnabled(!LODGenEnabled)} label={'Generate LODs'} /> + {LODGenEnabled && ( + <> + setLODImportFolder(event?.target.value)} + /> + + {selectedLODS.slice(0, 3).map((LOD, idx) => ( + inputSceneName.set(event.target.value)} + label={t('editor:dialog.saveNewScene.lbl-name')} + description={t('editor:dialog.saveNewScene.info-name')} + /> + + ) +} diff --git a/packages/editor/src/components/element/ElementList.tsx b/packages/editor/src/components/element/ElementList.tsx index 5d59736c126..c017617733a 100644 --- a/packages/editor/src/components/element/ElementList.tsx +++ b/packages/editor/src/components/element/ElementList.tsx @@ -31,58 +31,13 @@ import { useTranslation } from 'react-i18next' import InputText from '@etherealengine/client-core/src/common/components/InputText' import { Component } from '@etherealengine/ecs/src/ComponentFunctions' -import { VisualScriptComponent } from '@etherealengine/engine' -import { PositionalAudioComponent } from '@etherealengine/engine/src/audio/components/PositionalAudioComponent' -import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent' -import { GrabbableComponent } from '@etherealengine/engine/src/interaction/components/GrabbableComponent' -import { InteractableComponent } from '@etherealengine/engine/src/interaction/components/InteractableComponent' -import { AudioAnalysisComponent } from '@etherealengine/engine/src/scene/components/AudioAnalysisComponent' -import { CameraSettingsComponent } from '@etherealengine/engine/src/scene/components/CameraSettingsComponent' -import { EnvMapBakeComponent } from '@etherealengine/engine/src/scene/components/EnvMapBakeComponent' -import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' -import { GroundPlaneComponent } from '@etherealengine/engine/src/scene/components/GroundPlaneComponent' -import { ImageComponent } from '@etherealengine/engine/src/scene/components/ImageComponent' -import { LinkComponent } from '@etherealengine/engine/src/scene/components/LinkComponent' -import { MediaSettingsComponent } from '@etherealengine/engine/src/scene/components/MediaSettingsComponent' -import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' -import { MountPointComponent } from '@etherealengine/engine/src/scene/components/MountPointComponent' -import { ParticleSystemComponent } from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' -import { PortalComponent } from '@etherealengine/engine/src/scene/components/PortalComponent' -import { RenderSettingsComponent } from '@etherealengine/engine/src/scene/components/RenderSettingsComponent' -import { SDFComponent } from '@etherealengine/engine/src/scene/components/SDFComponent' -import { SceneDynamicLoadTagComponent } from '@etherealengine/engine/src/scene/components/SceneDynamicLoadTagComponent' -import { ScenePreviewCameraComponent } from '@etherealengine/engine/src/scene/components/ScenePreviewCamera' -import { SceneSettingsComponent } from '@etherealengine/engine/src/scene/components/SceneSettingsComponent' -import { ScreenshareTargetComponent } from '@etherealengine/engine/src/scene/components/ScreenshareTargetComponent' -import { ShadowComponent } from '@etherealengine/engine/src/scene/components/ShadowComponent' -import { SkyboxComponent } from '@etherealengine/engine/src/scene/components/SkyboxComponent' -import { SpawnPointComponent } from '@etherealengine/engine/src/scene/components/SpawnPointComponent' -import { SplineComponent } from '@etherealengine/engine/src/scene/components/SplineComponent' -import { SplineTrackComponent } from '@etherealengine/engine/src/scene/components/SplineTrackComponent' -import { SystemComponent } from '@etherealengine/engine/src/scene/components/SystemComponent' -import { TextComponent } from '@etherealengine/engine/src/scene/components/TextComponent' -import { VariantComponent } from '@etherealengine/engine/src/scene/components/VariantComponent' -import { VideoComponent } from '@etherealengine/engine/src/scene/components/VideoComponent' -import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' -import { defineState, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' -import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' -import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' -import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' -import { TriggerComponent } from '@etherealengine/spatial/src/physics/components/TriggerComponent' -import { AmbientLightComponent } from '@etherealengine/spatial/src/renderer/components/AmbientLightComponent' -import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/components/DirectionalLightComponent' -import { GroupComponent } from '@etherealengine/spatial/src/renderer/components/GroupComponent' -import { HemisphereLightComponent } from '@etherealengine/spatial/src/renderer/components/HemisphereLightComponent' -import { PointLightComponent } from '@etherealengine/spatial/src/renderer/components/PointLightComponent' -import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' -import { SpotLightComponent } from '@etherealengine/spatial/src/renderer/components/SpotLightComponent' +import { ComponentShelfCategoriesState } from '@etherealengine/editor/src/services/ComponentShelfCategoriesState' +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' import Icon from '@etherealengine/ui/src/primitives/mui/Icon' import Typography from '@etherealengine/ui/src/primitives/mui/Typography' - -import { PrimitiveGeometryComponent } from '../../../../engine/src/scene/components/PrimitiveGeometryComponent' import { ItemTypes } from '../../constants/AssetTypes' -import { ComponentEditorsState } from '../../functions/ComponentEditors' import { EditorControlFunctions } from '../../functions/EditorControlFunctions' +import { ComponentEditorsState } from '../../services/ComponentEditors' import { SelectionState } from '../../services/SelectionServices' import { usePopoverContextClose } from './PopoverContext' @@ -93,65 +48,6 @@ export type SceneElementType = { type: typeof ItemTypes.Component } -export const ComponentShelfCategoriesState = defineState({ - name: 'ee.editor.ComponentShelfCategories', - initial: () => { - return { - Files: [ - ModelComponent, - VolumetricComponent, - PositionalAudioComponent, - AudioAnalysisComponent, - VideoComponent, - ImageComponent - ], - 'Scene Composition': [ - PrimitiveGeometryComponent, - GroundPlaneComponent, - GroupComponent, - VariantComponent, - SceneDynamicLoadTagComponent - ], - Physics: [ColliderComponent, RigidBodyComponent, TriggerComponent], - Interaction: [ - SpawnPointComponent, - PortalComponent, - LinkComponent, - MountPointComponent, - InteractableComponent, - InputComponent, - GrabbableComponent - ], - Lighting: [ - AmbientLightComponent, - PointLightComponent, - SpotLightComponent, - DirectionalLightComponent, - HemisphereLightComponent - ], - FX: [ - LoopAnimationComponent, - ShadowComponent, - ParticleSystemComponent, - EnvmapComponent, - SDFComponent, - PostProcessingComponent - ], - Scripting: [SystemComponent, VisualScriptComponent], - Settings: [SceneSettingsComponent, RenderSettingsComponent, MediaSettingsComponent, CameraSettingsComponent], - Misc: [ - EnvMapBakeComponent, - ScenePreviewCameraComponent, - SkyboxComponent, - SplineTrackComponent, - SplineComponent, - TextComponent, - ScreenshareTargetComponent - ] - } as Record - } -}) - const ComponentListItem = ({ item }: { item: Component }) => { const { t } = useTranslation() useHookstate(getMutableState(ComponentEditorsState).keys).value // ensure reactively updates new components diff --git a/packages/editor/src/components/element/PropertiesPanelContainer.tsx b/packages/editor/src/components/element/PropertiesPanelContainer.tsx index b595085489f..6c11a9d2a02 100755 --- a/packages/editor/src/components/element/PropertiesPanelContainer.tsx +++ b/packages/editor/src/components/element/PropertiesPanelContainer.tsx @@ -33,7 +33,7 @@ import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materia import { NO_PROXY, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' import { GLTFNodeState } from '@etherealengine/engine/src/gltf/GLTFDocumentState' -import { ComponentEditorsState } from '../../functions/ComponentEditors' +import { ComponentEditorsState } from '../../services/ComponentEditors' import { EditorState } from '../../services/EditorServices' import { SelectionState } from '../../services/SelectionServices' import { PropertiesPanelButton } from '../inputs/Button' diff --git a/packages/editor/src/components/hierarchy/HierarchyTreeNode.tsx b/packages/editor/src/components/hierarchy/HierarchyTreeNode.tsx index bc5ad455923..b1b621e22c3 100644 --- a/packages/editor/src/components/hierarchy/HierarchyTreeNode.tsx +++ b/packages/editor/src/components/hierarchy/HierarchyTreeNode.tsx @@ -47,9 +47,9 @@ import { EntityTreeComponent, isAncestor } from '@etherealengine/spatial/src/tra import CircularProgress from '@etherealengine/ui/src/primitives/mui/CircularProgress' import { ItemTypes, SupportedFileTypes } from '../../constants/AssetTypes' -import { addMediaNode } from '../../functions/addMediaNode' -import { ComponentEditorsState } from '../../functions/ComponentEditors' import { EditorControlFunctions } from '../../functions/EditorControlFunctions' +import { addMediaNode } from '../../functions/addMediaNode' +import { ComponentEditorsState } from '../../services/ComponentEditors' import { SelectionState } from '../../services/SelectionServices' import useUpload from '../assets/useUpload' import TransformPropertyGroup from '../properties/TransformPropertyGroup' diff --git a/packages/editor/src/components/inputs/Vector3Input.tsx b/packages/editor/src/components/inputs/Vector3Input.tsx old mode 100755 new mode 100644 diff --git a/packages/editor/src/components/toolbar/Toolbar2.tsx b/packages/editor/src/components/toolbar/Toolbar2.tsx new file mode 100644 index 00000000000..8c00b49d7d7 --- /dev/null +++ b/packages/editor/src/components/toolbar/Toolbar2.tsx @@ -0,0 +1,152 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService' +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { RouterState } from '@etherealengine/client-core/src/common/services/RouterService' +import { GLTFModifiedState } from '@etherealengine/engine/src/gltf/GLTFDocumentState' +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' +import ContextMenu from '@etherealengine/ui/src/components/editor/layout/ContextMenu' +import Button from '@etherealengine/ui/src/primitives/tailwind/Button' +import { t } from 'i18next' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PiSquaresFourThin } from 'react-icons/pi' +import { inputFileWithAddToScene } from '../../functions/assetFunctions' +import { onNewScene } from '../../functions/sceneFunctions' +import { cmdOrCtrlString } from '../../functions/utils' +import { EditorState } from '../../services/EditorServices' +import ImportSettingsPanel from '../dialogs/ImportSettingsPanelDialog2' +import { SaveNewSceneDialog, SaveSceneDialog } from '../dialogs/SaveSceneDialog2' + +const onImportAsset = async () => { + const { projectName } = getState(EditorState) + + if (projectName) { + try { + await inputFileWithAddToScene({ projectName }) + } catch (err) { + NotificationService.dispatchNotify(err.message, { variant: 'error' }) + } + } +} + +const onCloseProject = () => { + const editorState = getMutableState(EditorState) + getMutableState(GLTFModifiedState).set({}) + editorState.projectName.set(null) + editorState.scenePath.set(null) + editorState.sceneName.set(null) + RouterState.navigate('/studio') + + const parsed = new URL(window.location.href) + const query = parsed.searchParams + + query.delete('project') + query.delete('scenePath') + + parsed.search = query.toString() + if (typeof history.pushState !== 'undefined') { + window.history.replaceState({}, '', parsed.toString()) + } +} + +const generateToolbarMenu = () => { + return [ + { + name: t('editor:menubar.newScene'), + action: onNewScene + }, + { + name: t('editor:menubar.saveScene'), + hotkey: `${cmdOrCtrlString}+s`, + action: () => PopoverState.showPopupover() + }, + { + name: t('editor:menubar.saveAs'), + action: () => PopoverState.showPopupover() + }, + { + name: t('editor:menubar.importSettings'), + action: () => PopoverState.showPopupover() + }, + { + name: t('editor:menubar.importAsset'), + action: onImportAsset + }, + { + name: t('editor:menubar.quit'), + action: onCloseProject + } + ] +} + +const toolbarMenu = generateToolbarMenu() + +export default function Toolbar() { + const { t } = useTranslation() + const anchorEl = useHookstate(null) + const anchorPosition = useHookstate({ left: 0, top: 0 }) + const anchorOpen = useHookstate(false) + + return ( + <> +
+ +
+ anchorOpen.set(false)} + > + {toolbarMenu.map(({ name, action, hotkey }, index) => ( +
+ +
+ ))} +
+ + ) +} diff --git a/packages/editor/src/constants/EditorModeTypes.ts b/packages/editor/src/constants/EditorModeTypes.ts new file mode 100644 index 00000000000..6a2b6662736 --- /dev/null +++ b/packages/editor/src/constants/EditorModeTypes.ts @@ -0,0 +1,31 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +export const EditorMode = { + Simple: 'Simple' as const, + Advanced: 'Advanced' as const +} + +export type EditorModeType = (typeof EditorMode)[keyof typeof EditorMode] diff --git a/packages/editor/src/pages/Editor2Page.tsx b/packages/editor/src/pages/Editor2Page.tsx new file mode 100644 index 00000000000..a36eca04ead --- /dev/null +++ b/packages/editor/src/pages/Editor2Page.tsx @@ -0,0 +1,79 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import '@etherealengine/client-core/src/networking/ClientNetworkingSystem' +import '@etherealengine/engine/src/EngineModule' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { loadEngineInjection } from '@etherealengine/projects/loadEngineInjection' +import { EngineState } from '@etherealengine/spatial/src/EngineState' +import React, { useEffect } from 'react' +import { useSearchParams } from 'react-router-dom' +import '../EditorModule' +import EditorContainer from '../components/Editor2Container' +import { EditorState } from '../services/EditorServices' +import { ProjectPage } from './ProjectPage' + +export const useStudioEditor = () => { + const engineReady = useHookstate(false) + + useEffect(() => { + getMutableState(EngineState).isEditing.set(true) + loadEngineInjection().then(() => { + engineReady.set(true) + }) + }, []) + + return engineReady.value +} + +export const EditorPage = () => { + const [params] = useSearchParams() + const { scenePath, projectName } = useHookstate(getMutableState(EditorState)) + + useEffect(() => { + const sceneInParams = params.get('scenePath') + if (sceneInParams) scenePath.set(sceneInParams) + const projectNameInParams = params.get('project') + if (projectNameInParams) projectName.set(projectNameInParams) + }, [params]) + + useEffect(() => { + if (!scenePath.value) return + + const parsed = new URL(window.location.href) + const query = parsed.searchParams + + query.set('scenePath', scenePath.value) + + parsed.search = query.toString() + if (typeof history.pushState !== 'undefined') { + window.history.replaceState({}, '', parsed.toString()) + } + }, [scenePath]) + + if (!scenePath.value && !projectName.value) return + + return +} diff --git a/packages/editor/src/pages/EditorPage.tsx b/packages/editor/src/pages/EditorPage.tsx index 56d81991cde..b78879ce24c 100644 --- a/packages/editor/src/pages/EditorPage.tsx +++ b/packages/editor/src/pages/EditorPage.tsx @@ -78,7 +78,7 @@ export const EditorPage = () => { } }, [scenePath]) - if (!scenePath.value && !projectName.value) return + if (!scenePath.value && !projectName.value) return return } diff --git a/packages/editor/src/functions/ComponentEditors.tsx b/packages/editor/src/services/ComponentEditors.tsx similarity index 75% rename from packages/editor/src/functions/ComponentEditors.tsx rename to packages/editor/src/services/ComponentEditors.tsx index e96fab0c6db..a3967d4e498 100644 --- a/packages/editor/src/functions/ComponentEditors.tsx +++ b/packages/editor/src/services/ComponentEditors.tsx @@ -59,69 +59,73 @@ import { VariantComponent } from '@etherealengine/engine/src/scene/components/Va import { VideoComponent } from '@etherealengine/engine/src/scene/components/VideoComponent' import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' import { defineState } from '@etherealengine/hyperflux' -import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' -import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' +import { + AmbientLightComponent, + DirectionalLightComponent, + HemisphereLightComponent, + PointLightComponent, + SpotLightComponent +} from '@etherealengine/spatial' import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' import { TriggerComponent } from '@etherealengine/spatial/src/physics/components/TriggerComponent' -import { AmbientLightComponent } from '@etherealengine/spatial/src/renderer/components/AmbientLightComponent' -import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/components/DirectionalLightComponent' import { FogSettingsComponent } from '@etherealengine/spatial/src/renderer/components/FogSettingsComponent' -import { HemisphereLightComponent } from '@etherealengine/spatial/src/renderer/components/HemisphereLightComponent' import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent' -import { PointLightComponent } from '@etherealengine/spatial/src/renderer/components/PointLightComponent' import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' -import { SpotLightComponent } from '@etherealengine/spatial/src/renderer/components/SpotLightComponent' import { PersistentAnchorComponent } from '@etherealengine/spatial/src/xr/XRAnchorComponents' -import AmbientLightNodeEditor from '../components/properties/AmbientLightNodeEditor' import { AudioAnalysisEditor } from '../components/properties/AudioAnalysisEditor' -import { CameraPropertiesNodeEditor } from '../components/properties/CameraPropertiesNodeEditor' -import ColliderComponentEditor from '../components/properties/ColliderComponentEditor' -import DirectionalLightNodeEditor from '../components/properties/DirectionalLightNodeEditor' -import EnvMapBakeNodeEditor from '../components/properties/EnvMapBakeNodeEditor' -import EnvMapEditor from '../components/properties/EnvMapEditor' import { FogSettingsEditor } from '../components/properties/FogSettingsEditor' import { GrabbableComponentNodeEditor } from '../components/properties/GrabbableComponentNodeEditor' -import GroundPlaneNodeEditor from '../components/properties/GroundPlaneNodeEditor' -import HemisphereLightNodeEditor from '../components/properties/HemisphereLightNodeEditor' -import ImageNodeEditor from '../components/properties/ImageNodeEditor' -import InputComponentNodeEditor from '../components/properties/InputComponentNodeEditor' import { InstancingNodeEditor } from '../components/properties/InstancingNodeEditor' import InteractableComponentNodeEditor from '../components/properties/InteractableComponentNodeEditor' -import LinkNodeEditor from '../components/properties/LinkNodeEditor' -import LoopAnimationNodeEditor from '../components/properties/LoopAnimationNodeEditor' -import MediaNodeEditor from '../components/properties/MediaNodeEditor' -import { MediaSettingsEditor } from '../components/properties/MediaSettingsEditor' -import { MeshNodeEditor } from '../components/properties/MeshNodeEditor' import ModelNodeEditor from '../components/properties/ModelNodeEditor' -import MountPointNodeEditor from '../components/properties/MountPointNodeEditor' import ParticleSystemNodeEditor from '../components/properties/ParticleSystemNodeEditor' import PersistentAnchorNodeEditor from '../components/properties/PersistentAnchorNodeEditor' -import PointLightNodeEditor from '../components/properties/PointLightNodeEditor' -import PortalNodeEditor from '../components/properties/PortalNodeEditor' -import PositionalAudioNodeEditor from '../components/properties/PositionalAudioNodeEditor' import { PostProcessingSettingsEditor } from '../components/properties/PostProcessingSettingsEditor' import PrimitiveGeometryNodeEditor from '../components/properties/PrimitiveGeometryNodeEditor' import { RenderSettingsEditor } from '../components/properties/RenderSettingsEditor' -import RigidBodyComponentEditor from '../components/properties/RigidbodyComponentEditor' -import ScenePreviewCameraNodeEditor from '../components/properties/ScenePreviewCameraNodeEditor' import { SceneSettingsEditor } from '../components/properties/SceneSettingsEditor' import ScreenshareTargetNodeEditor from '../components/properties/ScreenshareTargetNodeEditor' import SDFEditor from '../components/properties/SDFEditor' -import ShadowProperties from '../components/properties/ShadowProperties' -import SkyboxNodeEditor from '../components/properties/SkyboxNodeEditor' -import SpawnPointNodeEditor from '../components/properties/SpawnPointNodeEditor' -import { SplineNodeEditor } from '../components/properties/SplineNodeEditor' -import { SplineTrackNodeEditor } from '../components/properties/SplineTrackNodeEditor' -import SpotLightNodeEditor from '../components/properties/SpotLightNodeEditor' import SystemNodeEditor from '../components/properties/SystemNodeEditor' -import TextNodeEditor from '../components/properties/TextNodeEditor' -import TriggerComponentEditor from '../components/properties/TriggerComponentEditor' -import { EditorComponentType } from '../components/properties/Util' import { VariantNodeEditor } from '../components/properties/VariantNodeEditor' -import VideoNodeEditor from '../components/properties/VideoNodeEditor' -import { VisualScriptNodeEditor } from '../components/properties/VisualScriptNodeEditor' -import VolumetricNodeEditor from '../components/properties/VolumetricNodeEditor' + +// everythign above still needs to be built +import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' +import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' +import LoopAnimationNodeEditor from '@etherealengine/ui/src/components/editor/properties/animation' +import CameraPropertiesNodeEditor from '@etherealengine/ui/src/components/editor/properties/camera' +import ColliderComponentEditor from '@etherealengine/ui/src/components/editor/properties/collider' +import EnvMapEditor from '@etherealengine/ui/src/components/editor/properties/envmap' +import EnvMapBakeNodeEditor from '@etherealengine/ui/src/components/editor/properties/envMapBake' +import GroundPlaneNodeEditor from '@etherealengine/ui/src/components/editor/properties/groundPlane' +import ImageNodeEditor from '@etherealengine/ui/src/components/editor/properties/image' +import AmbientLightNodeEditor from '@etherealengine/ui/src/components/editor/properties/light/ambient' +import DirectionalLightNodeEditor from '@etherealengine/ui/src/components/editor/properties/light/directional' +import HemisphereLightNodeEditor from '@etherealengine/ui/src/components/editor/properties/light/hemisphere' +import PointLightNodeEditor from '@etherealengine/ui/src/components/editor/properties/light/point' +import SpotLightNodeEditor from '@etherealengine/ui/src/components/editor/properties/light/spot' +import LinkNodeEditor from '@etherealengine/ui/src/components/editor/properties/link' +import { default as MediaNodeEditor } from '@etherealengine/ui/src/components/editor/properties/media' +import MeshNodeEditor from '@etherealengine/ui/src/components/editor/properties/mesh' +import MountPointNodeEditor from '@etherealengine/ui/src/components/editor/properties/mountPoint' +import PortalNodeEditor from '@etherealengine/ui/src/components/editor/properties/portal' +import PositionalAudioNodeEditor from '@etherealengine/ui/src/components/editor/properties/positionalAudio' +import RigidBodyComponentEditor from '@etherealengine/ui/src/components/editor/properties/rigidBody' +import ScenePreviewCameraNodeEditor from '@etherealengine/ui/src/components/editor/properties/scenePreviewCamera' +import ShadowNodeEditor from '@etherealengine/ui/src/components/editor/properties/shadow' +import SkyboxNodeEditor from '@etherealengine/ui/src/components/editor/properties/skybox' +import SpawnPointNodeEditor from '@etherealengine/ui/src/components/editor/properties/spawnPoint' +import SplineNodeEditor from '@etherealengine/ui/src/components/editor/properties/spline' +import SplineTrackNodeEditor from '@etherealengine/ui/src/components/editor/properties/spline/track' +import TextNodeEditor from '@etherealengine/ui/src/components/editor/properties/text' +import TriggerComponentEditor from '@etherealengine/ui/src/components/editor/properties/trigger' +import VideoNodeEditor from '@etherealengine/ui/src/components/editor/properties/video' +import VisualScriptNodeEditor from '@etherealengine/ui/src/components/editor/properties/visualScript' +import VolumetricNodeEditor from '@etherealengine/ui/src/components/editor/properties/volumetric' +import InputComponentNodeEditor from '../components/properties/InputComponentNodeEditor' +import { MediaSettingsEditor } from '../components/properties/MediaSettingsEditor' +import { EditorComponentType } from '../components/properties/Util' export const ComponentEditorsState = defineState({ name: 'ee.editor.ComponentEditorsState', @@ -142,7 +146,7 @@ export const ComponentEditorsState = defineState({ [GroundPlaneComponent.name]: GroundPlaneNodeEditor, [MeshComponent.name]: MeshNodeEditor, [ModelComponent.name]: ModelNodeEditor, - [ShadowComponent.name]: ShadowProperties, + [ShadowComponent.name]: ShadowNodeEditor, [LoopAnimationComponent.name]: LoopAnimationNodeEditor, [ParticleSystemComponent.name]: ParticleSystemNodeEditor, [PrimitiveGeometryComponent.name]: PrimitiveGeometryNodeEditor, diff --git a/packages/editor/src/services/ComponentShelfCategoriesState.ts b/packages/editor/src/services/ComponentShelfCategoriesState.ts new file mode 100644 index 00000000000..cdb2ec428c0 --- /dev/null +++ b/packages/editor/src/services/ComponentShelfCategoriesState.ts @@ -0,0 +1,133 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Component } from '@etherealengine/ecs' +import { VisualScriptComponent } from '@etherealengine/engine' +import { PositionalAudioComponent } from '@etherealengine/engine/src/audio/components/PositionalAudioComponent' +import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent' +import { GrabbableComponent } from '@etherealengine/engine/src/interaction/components/GrabbableComponent' +import { InteractableComponent } from '@etherealengine/engine/src/interaction/components/InteractableComponent' +import { AudioAnalysisComponent } from '@etherealengine/engine/src/scene/components/AudioAnalysisComponent' +import { CameraSettingsComponent } from '@etherealengine/engine/src/scene/components/CameraSettingsComponent' +import { EnvMapBakeComponent } from '@etherealengine/engine/src/scene/components/EnvMapBakeComponent' +import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' +import { GroundPlaneComponent } from '@etherealengine/engine/src/scene/components/GroundPlaneComponent' +import { ImageComponent } from '@etherealengine/engine/src/scene/components/ImageComponent' +import { LinkComponent } from '@etherealengine/engine/src/scene/components/LinkComponent' +import { MediaSettingsComponent } from '@etherealengine/engine/src/scene/components/MediaSettingsComponent' +import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' +import { MountPointComponent } from '@etherealengine/engine/src/scene/components/MountPointComponent' +import { ParticleSystemComponent } from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { PortalComponent } from '@etherealengine/engine/src/scene/components/PortalComponent' +import { PrimitiveGeometryComponent } from '@etherealengine/engine/src/scene/components/PrimitiveGeometryComponent' +import { RenderSettingsComponent } from '@etherealengine/engine/src/scene/components/RenderSettingsComponent' +import { SDFComponent } from '@etherealengine/engine/src/scene/components/SDFComponent' +import { SceneDynamicLoadTagComponent } from '@etherealengine/engine/src/scene/components/SceneDynamicLoadTagComponent' +import { ScenePreviewCameraComponent } from '@etherealengine/engine/src/scene/components/ScenePreviewCamera' +import { SceneSettingsComponent } from '@etherealengine/engine/src/scene/components/SceneSettingsComponent' +import { ScreenshareTargetComponent } from '@etherealengine/engine/src/scene/components/ScreenshareTargetComponent' +import { ShadowComponent } from '@etherealengine/engine/src/scene/components/ShadowComponent' +import { SkyboxComponent } from '@etherealengine/engine/src/scene/components/SkyboxComponent' +import { SpawnPointComponent } from '@etherealengine/engine/src/scene/components/SpawnPointComponent' +import { SplineComponent } from '@etherealengine/engine/src/scene/components/SplineComponent' +import { SplineTrackComponent } from '@etherealengine/engine/src/scene/components/SplineTrackComponent' +import { SystemComponent } from '@etherealengine/engine/src/scene/components/SystemComponent' +import { TextComponent } from '@etherealengine/engine/src/scene/components/TextComponent' +import { VariantComponent } from '@etherealengine/engine/src/scene/components/VariantComponent' +import { VideoComponent } from '@etherealengine/engine/src/scene/components/VideoComponent' +import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' +import { defineState } from '@etherealengine/hyperflux' +import { + AmbientLightComponent, + DirectionalLightComponent, + HemisphereLightComponent, + PointLightComponent, + SpotLightComponent +} from '@etherealengine/spatial' +import { InputComponent } from '@etherealengine/spatial/src/input/components/InputComponent' +import { ColliderComponent } from '@etherealengine/spatial/src/physics/components/ColliderComponent' +import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' +import { TriggerComponent } from '@etherealengine/spatial/src/physics/components/TriggerComponent' +import { GroupComponent } from '@etherealengine/spatial/src/renderer/components/GroupComponent' +import { PostProcessingComponent } from '@etherealengine/spatial/src/renderer/components/PostProcessingComponent' + +export const ComponentShelfCategoriesState = defineState({ + name: 'ee.editor.ComponentShelfCategories', + initial: () => { + return { + Files: [ + ModelComponent, + VolumetricComponent, + PositionalAudioComponent, + AudioAnalysisComponent, + VideoComponent, + ImageComponent + ], + 'Scene Composition': [ + PrimitiveGeometryComponent, + GroundPlaneComponent, + GroupComponent, + VariantComponent, + SceneDynamicLoadTagComponent + ], + Physics: [ColliderComponent, RigidBodyComponent, TriggerComponent], + Interaction: [ + SpawnPointComponent, + PortalComponent, + LinkComponent, + MountPointComponent, + InteractableComponent, + InputComponent, + GrabbableComponent + ], + Lighting: [ + AmbientLightComponent, + PointLightComponent, + SpotLightComponent, + DirectionalLightComponent, + HemisphereLightComponent + ], + FX: [ + LoopAnimationComponent, + ShadowComponent, + ParticleSystemComponent, + EnvmapComponent, + SDFComponent, + PostProcessingComponent + ], + Scripting: [SystemComponent, VisualScriptComponent], + Settings: [SceneSettingsComponent, RenderSettingsComponent, MediaSettingsComponent, CameraSettingsComponent], + Misc: [ + EnvMapBakeComponent, + ScenePreviewCameraComponent, + SkyboxComponent, + SplineTrackComponent, + SplineComponent, + TextComponent, + ScreenshareTargetComponent + ] + } as Record + } +}) diff --git a/packages/editor/src/services/EditorHelperState.ts b/packages/editor/src/services/EditorHelperState.ts index 0e1d3a82774..6b61650a068 100644 --- a/packages/editor/src/services/EditorHelperState.ts +++ b/packages/editor/src/services/EditorHelperState.ts @@ -34,10 +34,12 @@ import { TransformSpaceType } from '@etherealengine/engine/src/scene/constants/transformConstants' import { defineState, syncStateWithLocalStorage } from '@etherealengine/hyperflux' +import { EditorMode, EditorModeType } from '../constants/EditorModeTypes' export const EditorHelperState = defineState({ name: 'EditorHelperState', initial: () => ({ + editorMode: EditorMode.Simple as EditorModeType, transformMode: TransformMode.translate as TransformModeType, transformModeOnCancel: TransformMode.translate as TransformModeType, transformSpace: TransformSpace.local as TransformSpaceType, diff --git a/packages/projects/default-project/xrengine.config.ts b/packages/projects/default-project/xrengine.config.ts index 888a63b7b12..00ee0762cf1 100644 --- a/packages/projects/default-project/xrengine.config.ts +++ b/packages/projects/default-project/xrengine.config.ts @@ -44,9 +44,12 @@ const config: ProjectConfigInterface = { '/auth': { component: () => import('@etherealengine/client/src/pages/auth/authRoutes') }, - '/studio': { + '/studio-old': { component: () => import('@etherealengine/client/src/pages/editor/editor') }, + '/studio': { + component: () => import('@etherealengine/client/src/pages/editor2') + }, '/room': { component: () => import('@etherealengine/client/src/pages/room') }, diff --git a/packages/server-core/src/route/route/route.seed.ts b/packages/server-core/src/route/route/route.seed.ts index ab06ee1b176..2cb2ecec755 100644 --- a/packages/server-core/src/route/route/route.seed.ts +++ b/packages/server-core/src/route/route/route.seed.ts @@ -56,6 +56,10 @@ export async function seed(knex: Knex): Promise { project: 'default-project', route: '/studio' }, + { + project: 'default-project', + route: '/studio-old' + }, { project: 'default-project', route: '/capture' diff --git a/packages/spatial/src/input/systems/ClientInputSystem.tsx b/packages/spatial/src/input/systems/ClientInputSystem.tsx index e202b9705ad..e331cbfa156 100755 --- a/packages/spatial/src/input/systems/ClientInputSystem.tsx +++ b/packages/spatial/src/input/systems/ClientInputSystem.tsx @@ -548,8 +548,8 @@ const CanvasInputReactor = () => { const pointerComponent = getOptionalComponent(emulatedInputSourceEntity, InputPointerComponent) if (!pointerComponent) return pointerComponent.position.set( - (event.clientX / window.innerWidth) * 2 - 1, - (event.clientY / window.innerHeight) * -2 + 1 + ((event.clientX - canvas.getBoundingClientRect().x) / canvas.clientWidth) * 2 - 1, + ((event.clientY - canvas.getBoundingClientRect().y) / canvas.clientHeight) * -2 + 1 ) } @@ -558,8 +558,8 @@ const CanvasInputReactor = () => { if (!pointerComponent) return const touch = event.touches[0] pointerComponent.position.set( - (touch.clientX / window.innerWidth) * 2 - 1, - (touch.clientY / window.innerHeight) * -2 + 1 + ((touch.clientX - canvas.getBoundingClientRect().x) / canvas.clientWidth) * 2 - 1, + ((touch.clientY - canvas.getBoundingClientRect().y) / canvas.clientHeight) * -2 + 1 ) } diff --git a/packages/ui/.storybook/preview.tsx b/packages/ui/.storybook/preview.tsx index 0f8e2b1612e..0eaeeaded6e 100644 --- a/packages/ui/.storybook/preview.tsx +++ b/packages/ui/.storybook/preview.tsx @@ -23,16 +23,17 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ +import { ThemeProvider } from '@etherealengine/client-core/src/common/services/ThemeService' import { Description, Primary, Stories, Subtitle, Title } from '@storybook/addon-docs' import { Preview } from '@storybook/react' import React, { lazy } from 'react' +import { DndProvider } from 'react-dnd' +import { HTML5Backend } from 'react-dnd-html5-backend' import { withRouter } from 'storybook-addon-react-router-v6' - -import { ThemeProvider } from '@etherealengine/client-core/src/common/services/ThemeService' - import '../../client/src/themes/base.css' import '../../client/src/themes/components.css' import '../../client/src/themes/utilities.css' +import '../src/fonts/font.css' const Engine = lazy(() => import('@etherealengine/client/src/engine')) @@ -42,7 +43,9 @@ export const decorators = [ return ( - + + + ) diff --git a/packages/ui/src/components/Chat/Create.tsx b/packages/ui/src/components/Chat/Create.tsx index 398548202fc..2ba8c334f10 100644 --- a/packages/ui/src/components/Chat/Create.tsx +++ b/packages/ui/src/components/Chat/Create.tsx @@ -56,7 +56,7 @@ export const Create = () => {
-
+
{ maxWidth={780} >
-
+

{props.message.text}

diff --git a/packages/ui/src/components/editor/input/Array/index.stories.tsx b/packages/ui/src/components/editor/input/Array/index.stories.tsx new file mode 100644 index 00000000000..ef2f0989408 --- /dev/null +++ b/packages/ui/src/components/editor/input/Array/index.stories.tsx @@ -0,0 +1,51 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Array', + component: Component, + parameters: { + componentSubtitle: 'ArrayInput', + jest: 'Array.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { + args: { + label: 'Source Path', + containerClassName: 'w-96', + values: ['test name 1', 'test value 2', 'test 3', 'test 4'], + inputLabel: 'Path' + } +} diff --git a/packages/ui/src/components/editor/input/Array/index.tsx b/packages/ui/src/components/editor/input/Array/index.tsx new file mode 100644 index 00000000000..cbb58bf6ede --- /dev/null +++ b/packages/ui/src/components/editor/input/Array/index.tsx @@ -0,0 +1,83 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import React from 'react' +import { HiPlus } from 'react-icons/hi2' +import { PiTrashSimple } from 'react-icons/pi' +import Input from '../../../../primitives/tailwind/Input' +import Label from '../../../../primitives/tailwind/Label' +import Text from '../../../../primitives/tailwind/Text' + +export interface ArrayInputProps { + name?: string + label: string + containerClassName?: string + values: string[] + onChange: (values: string[]) => void + inputLabel?: string +} + +// TODO: file and drag and drop functionality + +export default function ArrayInputGroup({ + name, + label, + containerClassName, + values, + onChange, + inputLabel +}: ArrayInputProps) { + const handleChange = (value: string, index: number, addRemove?: 'add' | 'remove') => { + if (addRemove === 'add') { + onChange([...values, value]) + } else if (addRemove === 'remove') { + onChange(values.filter((_, idx) => idx !== index)) + } else { + onChange(values.map((v, idx) => (idx === index ? value : v))) + } + } + + return ( +
+
+ {label} + handleChange('', 0, 'add')} /> +
+
+ {values.map((value, idx) => ( +
+ {inputLabel && } + handleChange(event.target.value, idx)} + /> + handleChange('', idx, 'remove')} /> +
+ ))} +
+
+ ) +} diff --git a/packages/ui/src/components/editor/input/Audio/index.stories.tsx b/packages/ui/src/components/editor/input/Audio/index.stories.tsx new file mode 100644 index 00000000000..068f4c8f662 --- /dev/null +++ b/packages/ui/src/components/editor/input/Audio/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Audio', + component: Component, + parameters: { + componentSubtitle: 'AudioInput', + jest: 'Audio.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component } diff --git a/packages/ui/src/components/editor/input/Audio/index.tsx b/packages/ui/src/components/editor/input/Audio/index.tsx new file mode 100644 index 00000000000..d5a0c279ea2 --- /dev/null +++ b/packages/ui/src/components/editor/input/Audio/index.tsx @@ -0,0 +1,36 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { AudioFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import React from 'react' +import { FileBrowserInput } from '../FileBrowser' +import { StringInputProps } from '../String' + +export function AudioInput({ ...rest }: StringInputProps) { + return +} + +export default AudioInput diff --git a/packages/ui/src/components/editor/input/Behavior/index.tsx b/packages/ui/src/components/editor/input/Behavior/index.tsx new file mode 100644 index 00000000000..876e388d536 --- /dev/null +++ b/packages/ui/src/components/editor/input/Behavior/index.tsx @@ -0,0 +1,510 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' +import { Texture, Vector2, Vector3 } from 'three' + +import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader' +import { + ApplyForceBehaviorJSON, + ApplySequencesJSON, + BehaviorJSON, + BehaviorJSONDefaults, + ChangeEmitDirectionBehaviorJSON, + ColorGeneratorJSON, + ColorOverLifeBehaviorJSON, + EmitSubParticleSystemBehaviorJSON, + FrameOverLifeBehaviorJSON, + GravityForceBehaviorJSON, + NoiseBehaviorJSON, + OrbitOverLifeBehaviorJSON, + Rotation3DOverLifeBehaviorJSON, + RotationGeneratorJSON, + RotationOverLifeBehaviorJSON, + SizeOverLifeBehaviorJSON, + SpeedOverLifeBehaviorJSON, + TextureSequencerJSON, + TurbulenceFieldBehaviorJSON, + ValueGeneratorJSON, + WidthOverLengthBehaviorJSON +} from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { State } from '@etherealengine/hyperflux' +import createReadableTexture from '@etherealengine/spatial/src/renderer/functions/createReadableTexture' +import BooleanInput from '../Boolean' +import ColorGenerator from '../Generator/Color' +import RotationGenerator from '../Generator/Rotation' +import ValueGenerator from '../Generator/Value' +import InputGroup from '../Group' +import NumericInput from '../Numeric' +import SelectInput from '../Select' +import Vector3Input from '../Vector3' + +export default function BehaviorInput({ + scope, + value, + onChange +}: { + scope: State + value: BehaviorJSON + onChange: (scope: State) => (value: any) => void +}) { + const onChangeBehaviorType = useCallback(() => { + const onChangeType = onChange(scope.type) + return (type: typeof value.type) => { + const nuVals = JSON.parse(JSON.stringify(BehaviorJSONDefaults[type])) + scope.set(nuVals) + onChangeType(type) + } + }, []) + + const onChangeVec3 = useCallback((scope: State) => { + const thisOnChange = onChange(scope) + return (vec3: Vector3) => { + thisOnChange([...vec3.toArray()]) + } + }, []) + + const applyForceInput = useCallback( + (scope: State) => { + const forceScope = scope as State + const value = forceScope.value + return ( + <> + + + + + + + + ) + }, + [scope] + ) + + const noiseInput = useCallback( + (scope: State) => { + const noiseScope = scope as State + const value = noiseScope.value + return ( + <> + + + + + + + + ) + }, + [scope] + ) + + const turbulenceFieldInput = useCallback( + (scope: State) => { + const turbulenceScope = scope as State + const value = turbulenceScope.value + return ( + <> + + + + + + + + + + + + + + ) + }, + [scope] + ) + + const gravityForceInput = useCallback( + (scope: State) => { + const gravityScope = scope as State + const value = gravityScope.value + return ( + <> + + + + + + + + ) + }, + [scope] + ) + + const colorOverLifeInput = useCallback( + (scope: State) => { + const colorScope = scope as State + const value = colorScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const rotationOverLifeInput = useCallback( + (scope: State) => { + const rotationScope = scope as State + const value = rotationScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const rotation3DOverLifeInput = useCallback( + (scope: State) => { + const rotation3DScope = scope as State + const rotation3D = rotation3DScope.value + return ( + <> + + + + + + + + ) + }, + [scope] + ) + + const sizeOverLifeInput = useCallback( + (scope: State) => { + const sizeScope = scope as State + const value = sizeScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const speedOverLifeInput = useCallback( + (scope: State) => { + const speedScope = scope as State + const value = speedScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const frameOverLifeInput = useCallback( + (scope: State) => { + const frameScope = scope as State + const value = frameScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const orbitOverLifeInput = useCallback( + (scope: State) => { + const orbitScope = scope as State + const value = orbitScope.value + return ( + <> + + + + + + + + ) + }, + [scope] + ) + + const widthOverLength = useCallback( + (scope: State) => { + const widthScope = scope as State + const value = widthScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const changeEmitDirectionInput = useCallback( + (scope: State) => { + const changeEmitDirectionScope = scope as State + const value = changeEmitDirectionScope.value + return ( + <> + + + + + ) + }, + [scope] + ) + + const emitSubParticleSystemInput = useCallback( + (scope: State) => { + const emitSubParticleSystemScope = scope as State + const value = emitSubParticleSystemScope.value + return ( + <> + + <> + {/* @todo */} + {/* */} + + + ) + }, + [scope] + ) + + const onChangeSequenceTexture = useCallback( + (scope: State) => { + const thisOnChange = onChange(scope.src) + return (src: string) => { + AssetLoader.load(src, (texture: Texture) => { + createReadableTexture(texture, { canvas: true, flipY: true }).then((readableTexture: Texture) => { + const canvas = readableTexture.image as HTMLCanvasElement + const ctx = canvas.getContext('2d')! + const width = canvas.width + const height = canvas.height + const imageData = ctx.getImageData(0, 0, width, height) + const locations: Vector2[] = [] + const threshold = scope.threshold.value + for (let i = 0; i < imageData.height; i++) { + for (let j = 0; j < imageData.width; j++) { + imageData.data[(i * imageData.width + j) * 4 + 3] > threshold && + locations.push(new Vector2(j, imageData.height - i)) + } + } + canvas.remove() + scope.locations.set(locations) + }) + }) + thisOnChange(src) + } + }, + [scope] + ) + + const onAddTextureSequencer = useCallback(() => { + const sequencersScope = scope as State + const sequencers = sequencersScope.value + const thisOnChange = onChange(sequencersScope.sequencers) + return () => { + const nuSequencer = { + range: { a: 0, b: 1 }, + sequencer: { + scaleX: 1, + scaleY: 1, + position: [0, 0, 0], + src: '', + locations: [], + threshold: 0.5 + } + } + const nuSequencers = [...JSON.parse(JSON.stringify(sequencers.sequencers)), nuSequencer] + thisOnChange(nuSequencers) + } + }, [scope]) + + // const applySequencesInput = useCallback( + // (scope: State) => { + // const applySequencesScope = scope as State + // const value = applySequencesScope.value + // return ( + // <> + // + // + // ) => { + // const sequencer = sequencerScope.value + // return ( + // <> + // + // + // + // + // + // + // + // + // + // + // + // ) + // }} + // /> + // + // ) + // }, + // [scope] + // ) + + const inputs = { + ApplyForce: applyForceInput, + Noise: noiseInput, + TurbulenceField: turbulenceFieldInput, + GravityForce: gravityForceInput, + ColorOverLife: colorOverLifeInput, + RotationOverLife: rotationOverLifeInput, + SizeOverLife: sizeOverLifeInput, + SpeedOverLife: speedOverLifeInput, + FrameOverLife: frameOverLifeInput, + OrbitOverLife: orbitOverLifeInput, + Rotation3DOverLife: rotation3DOverLifeInput, + WidthOverLength: widthOverLength, + ChangeEmitDirection: changeEmitDirectionInput, + EmitSubParticleSystem: emitSubParticleSystemInput + } + + return ( + <> + + + + {inputs[value.type](scope)} + + ) +} diff --git a/packages/ui/src/components/editor/input/Boolean/index.stories.tsx b/packages/ui/src/components/editor/input/Boolean/index.stories.tsx new file mode 100644 index 00000000000..0486cd2abc4 --- /dev/null +++ b/packages/ui/src/components/editor/input/Boolean/index.stories.tsx @@ -0,0 +1,47 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component, { BooleanInputProp } from './index' + +const argTypes: BooleanInputProp = { value: false, onChange: () => {} } + +export default { + title: 'Editor/Input/Boolean', + component: Component, + parameters: { + componentSubtitle: 'BooleanInput', + jest: 'Boolean.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { + args: { + value: false + } +} diff --git a/packages/ui/src/components/editor/input/Boolean/index.tsx b/packages/ui/src/components/editor/input/Boolean/index.tsx new file mode 100644 index 00000000000..a299ab4002a --- /dev/null +++ b/packages/ui/src/components/editor/input/Boolean/index.tsx @@ -0,0 +1,59 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { twMerge } from 'tailwind-merge' +import Checkbox from '../../../../primitives/tailwind/Checkbox' + +export interface BooleanInputProp { + value: boolean + onChange: (value: boolean) => void + onRelease?: (value: boolean) => void + disabled?: boolean + className?: string +} + +export const BooleanInput = (props: BooleanInputProp) => { + const onBlur = () => { + if (props.onRelease) props.onRelease(props.value) + } + + return ( + + ) +} + +export default BooleanInput diff --git a/packages/ui/src/components/editor/input/Euler/index.stories.tsx b/packages/ui/src/components/editor/input/Euler/index.stories.tsx new file mode 100644 index 00000000000..dce86acc043 --- /dev/null +++ b/packages/ui/src/components/editor/input/Euler/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Euler', + component: Component, + parameters: { + componentSubtitle: 'EulerInput', + jest: 'Euler.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Euler/index.tsx b/packages/ui/src/components/editor/input/Euler/index.tsx new file mode 100644 index 00000000000..4295d7e350c --- /dev/null +++ b/packages/ui/src/components/editor/input/Euler/index.tsx @@ -0,0 +1,117 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { useHookstate } from '@etherealengine/hyperflux' +import { Q_IDENTITY } from '@etherealengine/spatial/src/common/constants/MathConstants' +import React, { useCallback, useEffect } from 'react' +import { Euler, Quaternion, MathUtils as _Math } from 'three' +import NumericInput from '../Numeric' +import { Vector3Scrubber } from '../Vector3' + +const { RAD2DEG, DEG2RAD } = _Math +/** + * Type aliase created EulerInputProps. + * + * @type {Object} + */ +type EulerInputProps = { + quaternion: Quaternion + onChange?: (euler: Euler) => any + onRelease?: () => void + unit?: string +} + +/** + * FileIEulerInputnput used to show EulerInput. + * + * @type {Object} + */ +export const EulerInput = (props: EulerInputProps) => { + const euler = useHookstate(new Euler().setFromQuaternion(props.quaternion, 'YXZ')) + + useEffect(() => { + euler.value.setFromQuaternion(props.quaternion, 'YXZ') + }, [props]) + + const onSetEuler = useCallback( + (component: keyof typeof euler) => (value: number) => { + const radVal = value * DEG2RAD + euler[component].value !== radVal && (euler[component].set(radVal) || props.onChange?.(euler.value)) + }, + [] + ) + + return ( +
+ props.onRelease?.()} + unit={props.unit} + prefix={ + + } + /> + props.onRelease?.()} + unit={props.unit} + prefix={ + + } + /> + props.onRelease?.()} + unit={props.unit} + prefix={ + + } + /> +
+ ) +} + +EulerInput.defaultProps = { + quaternion: Q_IDENTITY +} +export default EulerInput diff --git a/packages/ui/src/components/editor/input/FileBrowser/index.stories.tsx b/packages/ui/src/components/editor/input/FileBrowser/index.stories.tsx new file mode 100644 index 00000000000..e91a9de7907 --- /dev/null +++ b/packages/ui/src/components/editor/input/FileBrowser/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/FileBrowser', + component: Component, + parameters: { + componentSubtitle: 'FileBrowserInput', + jest: 'FileBrowser.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/FileBrowser/index.tsx b/packages/ui/src/components/editor/input/FileBrowser/index.tsx new file mode 100644 index 00000000000..67e9db394b0 --- /dev/null +++ b/packages/ui/src/components/editor/input/FileBrowser/index.tsx @@ -0,0 +1,133 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useDrop } from 'react-dnd' + +import config from '@etherealengine/common/src/config' +//import useUpload from '../assets/useUpload' +import useUpload from '@etherealengine/editor/src/components/assets/useUpload' +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { AllFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import { ControlledStringInput, StringInputProps } from '../String' + +export type FileBrowserInputProps = StringInputProps & { acceptFileTypes: string[]; acceptDropItems: string[] } + +/** + * Function component used for rendering FileBrowserInput. + */ +export function FileBrowserInput({ + onRelease, + value, + acceptFileTypes, + acceptDropItems, + ...rest +}: FileBrowserInputProps) { + const uploadOptions = { + multiple: false, + accepts: acceptFileTypes + } + const onUpload = useUpload(uploadOptions) + + // todo fix for invalid URLs + const assetIsExternal = value && !value?.includes(config.client.fileServer) && !value.includes('blob:https://') + const uploadExternalAsset = () => { + onUpload([ + { + isFile: true, + name: value?.split('/').pop(), + file: async (onSuccess, onFail) => { + try { + const asset = await fetch(value!) + const blob = await asset.blob() + const file = new File([blob], value!.split('/').pop()!) + onSuccess(file) + } catch (error) { + if (onFail) onFail(error) + else throw error + } + } + } as Partial + ] as any).then((assets) => { + if (assets) { + onRelease?.(assets[0]) + } + }) + } + + const [{ canDrop, isOver }, dropRef] = useDrop({ + accept: [...acceptDropItems, ItemTypes.File], + async drop(item: any, monitor) { + const isDropType = acceptDropItems.find((element) => element === item.type) + if (isDropType) { + // Below url fix is applied when item is folder + let url = item.url + if (!url.endsWith(item.fullName)) { + url += item.fullName + } + + onRelease?.(url) + } else { + // https://github.com/react-dnd/react-dnd/issues/1345#issuecomment-538728576 + const dndItem: any = monitor.getItem() + const entries = Array.from(dndItem.items).map((item: any) => item.webkitGetAsEntry()) + + onUpload(entries).then((assets) => { + if (assets) { + onRelease?.(assets[0]) + } + }) + } + }, + collect: (monitor) => ({ + canDrop: monitor.canDrop(), + isOver: monitor.isOver() + }) + }) + + return ( + <> + + {/*assetIsExternal && ( + + + + )*/} + + ) +} + +FileBrowserInput.defaultProps = { + acceptFileTypes: AllFileTypes, + acceptDropItems: AllFileTypes +} + +export default FileBrowserInput diff --git a/packages/ui/src/components/editor/input/Folder/index.stories.tsx b/packages/ui/src/components/editor/input/Folder/index.stories.tsx new file mode 100644 index 00000000000..eed845941a8 --- /dev/null +++ b/packages/ui/src/components/editor/input/Folder/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Folder', + component: Component, + parameters: { + componentSubtitle: 'FolderInput', + jest: 'Folder.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Folder/index.tsx b/packages/ui/src/components/editor/input/Folder/index.tsx new file mode 100644 index 00000000000..5cbc1daac8d --- /dev/null +++ b/packages/ui/src/components/editor/input/Folder/index.tsx @@ -0,0 +1,38 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { AllFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import React from 'react' +import FileBrowserInput from '../FileBrowser' +import { StringInputProps } from '../String' + +export function FolderInput({ ...rest }: StringInputProps) { + return +} + +FolderInput.defaultProps = {} + +export default FolderInput diff --git a/packages/ui/src/components/editor/input/Generator/Color/index.tsx b/packages/ui/src/components/editor/input/Generator/Color/index.tsx new file mode 100644 index 00000000000..8078c8e4857 --- /dev/null +++ b/packages/ui/src/components/editor/input/Generator/Color/index.tsx @@ -0,0 +1,235 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' +import { Color } from 'three' + +import { + ColorGeneratorJSON, + ColorGeneratorJSONDefaults, + ColorGradientFunctionJSON, + ColorGradientJSON, + ColorJSON, + ColorRangeJSON, + ConstantColorJSON, + RandomColorJSON +} from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { State } from '@etherealengine/hyperflux/functions/StateFunctions' +import Typography from '@etherealengine/ui/src/primitives/mui/Typography' + +import { Grid } from '@mui/material' +import Button from '../../../../../primitives/tailwind/Button' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../Group' +import NumericInput from '../../Numeric' +import SelectInput from '../../Select' + +export function ColorJSONInput({ value, onChange }: { value: ColorJSON; onChange: (color: ColorJSON) => void }) { + return ( + <> + + { + onChange({ r: color.r, g: color.g, b: color.b, a: value.a }) + }} + /> + + + onChange({ r: value.r, g: value.g, b: value.b, a: alpha })} + /> + + + ) +} + +export default function ColorGenerator({ + scope, + value, + onChange +}: { + scope: State + value: ColorGeneratorJSON + onChange: (scope: State) => (value: any) => void +}) { + const onChangeType = useCallback(() => { + const thisOnChange = onChange(scope.type) + return (type: typeof value.type) => { + scope.set(ColorGeneratorJSONDefaults[type]) + thisOnChange(type) + } + }, []) + + const ConstantColorInput = useCallback(() => { + const constantScope = scope as State + const constant = constantScope.value + return + }, [scope]) + + const ColorRangeInput = useCallback(() => { + const rangeScope = scope as State + const range = rangeScope.value + return ( + <> + + + + + + + + ) + }, [scope]) + + const RandomColorInput = useCallback(() => { + const randomScope = scope as State + const random = randomScope.value + return ( + <> + + + + + + + + ) + }, [scope]) + + const onRemoveGradient = useCallback((element: State) => { + const gradientScope = scope as State + const gradient = gradientScope.value + const thisOnChange = onChange(gradientScope.functions) + return () => { + const nuFunctions = gradient.functions.filter((item) => item !== element.value) + thisOnChange(JSON.parse(JSON.stringify(nuFunctions))) + } + }, []) + + const GradientInput = useCallback(() => { + const gradientScope = scope as State + const gradient = gradientScope.value + return ( +
+ + + {gradient.functions.map((item, index) => ( +
+ + + Start + + + + + + A + + + + + + B + + + + + + +
+ ))} +
+ ) + }, [scope]) + + const colorInputs = { + ConstantColor: ConstantColorInput, + ColorRange: ColorRangeInput, + RandomColor: RandomColorInput, + Gradient: GradientInput + } + + return ( +
+ + + + {colorInputs[value.type]()} +
+ ) +} diff --git a/packages/ui/src/components/editor/input/Generator/Rotation/index.tsx b/packages/ui/src/components/editor/input/Generator/Rotation/index.tsx new file mode 100644 index 00000000000..135cb55bf04 --- /dev/null +++ b/packages/ui/src/components/editor/input/Generator/Rotation/index.tsx @@ -0,0 +1,129 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' +import { Vector3 } from 'three' + +import { + AxisAngleGeneratorJSON, + EulerGeneratorJSON, + RotationGeneratorJSON, + RotationGeneratorJSONDefaults, + ValueGeneratorJSON +} from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { State } from '@etherealengine/hyperflux' +import InputGroup from '../../Group' +import SelectInput from '../../Select' +import Vector3Input from '../../Vector3' +import ValueGenerator from '../Value' + +export default function RotationGenerator({ + scope, + value, + onChange +}: { + scope: State + value: RotationGeneratorJSON + onChange: (scope: State) => (value: any) => void +}) { + const onChangeVec3 = useCallback((scope: State) => { + const thisOnChange = onChange(scope) + return (vec3: Vector3) => { + thisOnChange([...vec3.toArray()]) + } + }, []) + + const AxisAngleGeneratorInput = useCallback(() => { + const axisAngleScope = scope as State + const axisAngle = axisAngleScope.value + return ( + <> + + + + + + + + ) + }, []) + + const EulerGeneratorInput = useCallback(() => { + const eulerScope = scope as State + const euler = eulerScope.value + return ( + <> + + + + + + + + + + + ) + }, []) + + const RandomQuatGeneratorInput = useCallback(() => { + return <> + }, []) + + const onChangeRotationType = useCallback(() => { + const thisOnChange = onChange(scope.type) + return (type: typeof value.type) => { + scope.set(RotationGeneratorJSONDefaults[type]) + thisOnChange(type) + } + }, []) + + const rotationInputs = { + AxisAngle: AxisAngleGeneratorInput, + Euler: EulerGeneratorInput, + RandomQuat: RandomQuatGeneratorInput + } + + return ( + <> + + + + {rotationInputs[value.type]()} + + ) +} diff --git a/packages/ui/src/components/editor/input/Generator/Value/index.tsx b/packages/ui/src/components/editor/input/Generator/Value/index.tsx new file mode 100644 index 00000000000..e07ffabea69 --- /dev/null +++ b/packages/ui/src/components/editor/input/Generator/Value/index.tsx @@ -0,0 +1,198 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' + +import { + BezierFunctionJSON, + ConstantValueJSON, + IntervalValueJSON, + PiecewiseBezierValueJSON, + ValueGeneratorJSON, + ValueGeneratorJSONDefaults +} from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { State } from '@etherealengine/hyperflux/functions/StateFunctions' +import Button from '../../../../../primitives/tailwind/Button' +import InputGroup from '../../Group' +import NumericInput from '../../Numeric' +import SelectInput from '../../Select' + +export default function ValueGenerator({ + scope, + value, + onChange +}: { + scope: State + value: ValueGeneratorJSON + onChange: (scope: State) => (value: any) => void +}) { + const onChangeType = useCallback(() => { + const thisOnChange = onChange(scope.type) + return (type: typeof value.type) => { + scope.set(JSON.parse(JSON.stringify(ValueGeneratorJSONDefaults[type]))) + thisOnChange(type) + } + }, []) + + const onAddBezier = useCallback(() => { + const bezierScope = scope as State + const thisOnChange = onChange(bezierScope.functions) + return () => { + const bezierJson = value as PiecewiseBezierValueJSON + const nuFunctions = [ + ...JSON.parse(JSON.stringify(bezierJson.functions)), + { + function: { + p0: 0, + p1: 0, + p2: 1, + p3: 1 + }, + start: 0 + } as BezierFunctionJSON + ] + thisOnChange(nuFunctions) + } + }, []) + + const onRemoveBezier = useCallback((element: State) => { + const bezierScope = scope as State + const bezier = bezierScope.value + const thisOnChange = onChange(bezierScope.functions) + return () => { + const nuFunctions = bezier.functions.filter((f) => f !== element.value) + thisOnChange(JSON.parse(JSON.stringify(nuFunctions))) + } + }, []) + + const ConstantInput = useCallback(() => { + const constantScope = scope as State + const constant = constantScope.value + return ( + <> + + + + + ) + }, [scope]) + + const IntervalInput = useCallback(() => { + const intervalScope = scope as State + const interval = intervalScope.value + return ( + <> + + + + + + + + ) + }, [scope]) + + const BezierInput = useCallback(() => { + const bezierScope = scope as State + return ( +
+ + + {/*) => ( +
+ + + + +
+
+
+ +
+ +
+ )} + />*/} +
+ ) + }, [scope]) + + const valueInputs = { + ConstantValue: ConstantInput, + IntervalValue: IntervalInput, + PiecewiseBezier: BezierInput + } + + return ( +
+ + + + {valueInputs[value.type]()} +
+ ) +} diff --git a/packages/ui/src/components/editor/input/Group/index.stories.tsx b/packages/ui/src/components/editor/input/Group/index.stories.tsx new file mode 100644 index 00000000000..848ad6283df --- /dev/null +++ b/packages/ui/src/components/editor/input/Group/index.stories.tsx @@ -0,0 +1,48 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Group', + component: Component, + parameters: { + componentSubtitle: 'InputGroup', + jest: 'Group.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { + args: { + label: 'group label', + info: 'input group info' + } +} diff --git a/packages/ui/src/components/editor/input/Group/index.tsx b/packages/ui/src/components/editor/input/Group/index.tsx new file mode 100644 index 00000000000..5dfbdf22a09 --- /dev/null +++ b/packages/ui/src/components/editor/input/Group/index.tsx @@ -0,0 +1,144 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import HelpOutlineIcon from '@mui/icons-material/HelpOutline' +import { CiCircleInfo } from 'react-icons/ci' +import { twMerge } from 'tailwind-merge' +import Label from '../../../../primitives/tailwind/Label' +import Tooltip from '../../../../primitives/tailwind/Tooltip' +import { InfoTooltip } from '../../layout/Tooltip' + +/** + * Used to provide styles for InputGroupContainer div. + */ +export const InputGroupContainer = ({ disabled = false, children, ...rest }) => ( +
+ {children} +
+) + +/** + * Used to provide styles for InputGroupContent div. + */ +export const InputGroupContent = ({ extraClassName = '', children }) => ( +
label]:block [&>label]:w-[35%] [&>label]:pb-0.5 [&>label]:pt-1 [&>label]:text-neutral-400', + "font-['Figtree'] text-xs font-normal text-neutral-400", + '[&>*:first-child]:max-w-[calc(100%_-_2px)]', + extraClassName + )} + > + {children} +
+) + +export const InputGroupVerticalContainer = ({ disabled = false, children }) => ( +
label]:block [&>label]:w-[35%] [&>label]:pb-0.5 [&>label]:pt-1 [&>label]:text-[color:var(--textColor)]' + )} + > + {children} +
+) + +export const InputGroupVerticalContainerWide = ({ disabled = false, children }) => ( +
label]:block [&>label]:w-full [&>label]:pb-0.5 [&>label]:pt-1 [&>label]:text-[color:var(--textColor)]' + )} + > + {children} +
+) + +export const InputGroupVerticalContent = ({ children }) =>
{children}
+/** + * Used to provide styles for InputGroupInfoIcon div. + */ +// change later +// .info text-[color:var(--textColor)] h-4 w-auto ml-[5px] +export const InputGroupInfoIcon = ({ onClick = () => {} }) => ( + +) + +interface InputGroupInfoProp { + info: string | JSX.Element +} + +/** + * Used to render InfoTooltip component. + */ +export function InputGroupInfo({ info }: InputGroupInfoProp) { + return ( + + + + ) +} + +export interface InputGroupProps { + name?: string + label: string + info?: string + disabled?: boolean + children: React.ReactNode + className?: string + labelClassName?: string + infoClassName?: string +} + +/** + * InputGroup used to render the view of component. + */ +export function InputGroup({ children, info, label, className, labelClassName, infoClassName }: InputGroupProps) { + return ( +
+ + {info && ( + + + + )} + {children} +
+ ) +} + +export default InputGroup diff --git a/packages/ui/src/components/editor/input/Image/Preview/index.stories.tsx b/packages/ui/src/components/editor/input/Image/Preview/index.stories.tsx new file mode 100644 index 00000000000..b78f3bfa0e7 --- /dev/null +++ b/packages/ui/src/components/editor/input/Image/Preview/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/ImagePreview', + component: Component, + parameters: { + componentSubtitle: 'ImagePreviewInput', + jest: 'ImagePreview.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Image/Preview/index.tsx b/packages/ui/src/components/editor/input/Image/Preview/index.tsx new file mode 100644 index 00000000000..2e16a582311 --- /dev/null +++ b/packages/ui/src/components/editor/input/Image/Preview/index.tsx @@ -0,0 +1,77 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import ImageInput from '..' +import InputGroup from '../../Group' +import { StringInputProps } from '../../String' + +export const ImageContainer = ({ children }) => { + return
{children}
+} + +export default function ImagePreviewInput({ + value, + onRelease, + label, + previewOnly, + ...rest +}: StringInputProps & { label?: string; previewOnly?: boolean }) { + return ( + + {label && ( +
{label}
+ )} +
+
+
+
+ +
+
+
+ {(previewOnly === undefined || previewOnly === false) && ( +
+ +
+ )} +
+
+ ) +} + +ImagePreviewInput.defaultProps = { + value: 'https://fastly.picsum.photos/id/1065/200/300.jpg?hmac=WvioY_uR2xNPKNxQoR9y1HhWkuV6_v7rB23clZYh0Ks', + onRelease: () => {} +} + +export function ImagePreviewInputGroup({ name, label, value, onRelease, ...rest }) { + return ( + + + + ) +} diff --git a/packages/ui/src/components/editor/input/Image/index.stories.tsx b/packages/ui/src/components/editor/input/Image/index.stories.tsx new file mode 100644 index 00000000000..b9eafd89650 --- /dev/null +++ b/packages/ui/src/components/editor/input/Image/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Image', + component: Component, + parameters: { + componentSubtitle: 'ImageInput', + jest: 'Image.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component } diff --git a/packages/ui/src/components/editor/input/Image/index.tsx b/packages/ui/src/components/editor/input/Image/index.tsx new file mode 100644 index 00000000000..8d43816cb45 --- /dev/null +++ b/packages/ui/src/components/editor/input/Image/index.tsx @@ -0,0 +1,36 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { ImageFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import React from 'react' +import FileBrowserInput from '../FileBrowser' +import { StringInputProps } from '../String' + +export function ImageInput({ ...rest }: StringInputProps) { + return +} + +export default ImageInput diff --git a/packages/ui/src/components/editor/input/Material/index.stories.tsx b/packages/ui/src/components/editor/input/Material/index.stories.tsx new file mode 100644 index 00000000000..1208b1b1fbe --- /dev/null +++ b/packages/ui/src/components/editor/input/Material/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Material', + component: Component, + parameters: { + componentSubtitle: 'MaterialInput', + jest: 'Material.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Material/index.tsx b/packages/ui/src/components/editor/input/Material/index.tsx new file mode 100644 index 00000000000..d179b7f3a22 --- /dev/null +++ b/packages/ui/src/components/editor/input/Material/index.tsx @@ -0,0 +1,65 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { DropTargetMonitor, useDrop } from 'react-dnd' + +import { EntityUUID } from '@etherealengine/ecs' +import { Entity } from '@etherealengine/ecs/src/Entity' +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { ControlledStringInput } from '../String' + +export function MaterialInput void }>({ + value, + onChange, + ...rest +}: T) { + function onDrop(item: { value?: Entity | Entity[] | undefined }, _monitor: DropTargetMonitor) { + let element = item.value + if (typeof element === 'undefined') return + if (Array.isArray(value)) { + element = element[0] + } + if (typeof element !== 'string') return + onChange(element) + } + + const [{ canDrop, isOver }, dropRef] = useDrop({ + accept: [ItemTypes.Material], + drop: onDrop, + collect: (monitor) => ({ + canDrop: monitor.canDrop(), + isOver: monitor.isOver() + }) + }) + + return ( + + ) +} + +MaterialInput.defaultProps = {} + +export default MaterialInput diff --git a/packages/ui/src/components/editor/input/Model/index.stories.tsx b/packages/ui/src/components/editor/input/Model/index.stories.tsx new file mode 100644 index 00000000000..8b6a58d235d --- /dev/null +++ b/packages/ui/src/components/editor/input/Model/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Model', + component: Component, + parameters: { + componentSubtitle: 'ModelInput', + jest: 'Model.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Model/index.tsx b/packages/ui/src/components/editor/input/Model/index.tsx new file mode 100644 index 00000000000..0c4508e5b5e --- /dev/null +++ b/packages/ui/src/components/editor/input/Model/index.tsx @@ -0,0 +1,45 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { ModelFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import React from 'react' +import FileBrowserInput from '../FileBrowser' +import { StringInputProps } from '../String' + +export function ModelInput({ onRelease, ...rest }: StringInputProps) { + return ( + + ) +} + +ModelInput.defaultProps = {} + +export default ModelInput diff --git a/packages/ui/src/components/editor/input/Numeric/Stepper/index.stories.tsx b/packages/ui/src/components/editor/input/Numeric/Stepper/index.stories.tsx new file mode 100644 index 00000000000..985cf608ff6 --- /dev/null +++ b/packages/ui/src/components/editor/input/Numeric/Stepper/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Numeric/Stepper', + component: Component, + parameters: { + componentSubtitle: 'NumericStepperInput', + jest: 'NumericStepper.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx b/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx new file mode 100644 index 00000000000..608e3c643e0 --- /dev/null +++ b/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx @@ -0,0 +1,109 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import ArrowLeftIcon from '@mui/icons-material/ArrowLeft' +import ArrowRightIcon from '@mui/icons-material/ArrowRight' + +import { t } from 'i18next' +import NumericInput, { NumericInputProp } from '..' +import { InfoTooltip } from '../../../layout/Tooltip' + +const stepperInputContainerStyle = { + display: 'flex', + flex: '1', + width: '100%', + height: '24px' +} + +const stepperButtonStyle = { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + backgroundColor: 'var(--toolbar)', + border: '1px solid var(--inputOutline)', + color: 'var(--textColor)', + width: '20px', + padding: '0', + margin: 0 +} + +const leftStepperButtonStyle = { + ...stepperButtonStyle, + borderTopLeftRadius: '4px', + borderBottomLeftRadius: '4px' +} + +const rightStepperButtonStyle = { + ...stepperButtonStyle, + borderTopRightRadius: '4px', + borderBottomRightRadius: '4px' +} + +export function NumericStepperInput({ + style, + className, + decrementTooltip, + incrementTooltip, + onChange, + value, + mediumStep, + ...rest +}: { + style?: React.CSSProperties + className?: string + incrementTooltip?: string + decrementTooltip?: string + onChange: (val) => void + value: number + mediumStep: number +} & NumericInputProp) { + const onIncrement = () => onChange(value + mediumStep) + const onDecrement = () => onChange(value - mediumStep) + + return ( +
+ + + + + + + +
+ ) +} + +NumericStepperInput.defaultProps = { + incrementTooltip: t('editor:toolbar.grid.info-incrementHeight'), + decrementTooltip: t('editor:toolbar.grid.info-decrementHeight') +} + +export default NumericStepperInput diff --git a/packages/ui/src/components/editor/input/Numeric/index.stories.tsx b/packages/ui/src/components/editor/input/Numeric/index.stories.tsx new file mode 100644 index 00000000000..42cc0a3097d --- /dev/null +++ b/packages/ui/src/components/editor/input/Numeric/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Numeric', + component: Component, + parameters: { + componentSubtitle: 'NumericInput', + jest: 'Numeric.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Numeric/index.tsx b/packages/ui/src/components/editor/input/Numeric/index.tsx new file mode 100644 index 00000000000..0c0dbf439f6 --- /dev/null +++ b/packages/ui/src/components/editor/input/Numeric/index.tsx @@ -0,0 +1,161 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { clamp } from '@etherealengine/spatial/src/common/functions/MathLerpFunctions' + +import { getStepSize, toPrecision } from '@etherealengine/editor/src/functions/utils' +import { twMerge } from 'tailwind-merge' +import Text from '../../../../primitives/tailwind/Text' + +function toPrecisionString(value: number, precision?: number) { + if (precision && precision <= 1) { + const numDigits = Math.abs(Math.log10(precision)) + const minimumFractionDigits = Math.min(numDigits, 2) + const maximumFractionDigits = Math.max(minimumFractionDigits, numDigits) + + return value.toLocaleString('fullwide', { + minimumFractionDigits, + maximumFractionDigits, + useGrouping: false + }) + } + return value.toLocaleString('fullwide', { useGrouping: false }) +} + +export interface NumericInputProp extends Omit, 'onChange' | 'prefix'> { + value: number + onChange: (value: number) => void + onRelease?: (value: number) => void + className?: string + inputClassName?: string + unit?: string + prefix?: React.ReactNode + displayPrecision?: number + precision?: number + mediumStep?: number + smallStep?: number + largeStep?: number + min?: number + max?: number +} + +const NumericInput = ({ + className, + inputClassName, + unit, + prefix, + displayPrecision, + value, + precision, + mediumStep, + onChange, + onRelease, + smallStep, + largeStep, + min, + max, + ...rest +}: NumericInputProp) => { + const handleStep = (event: React.KeyboardEvent, direction: number) => { + const stepSize = event ? getStepSize(event, smallStep, mediumStep, largeStep) : mediumStep + + const nextValue = value + stepSize * direction + const clampedValue = min != null && max != null ? clamp(nextValue, min, max) : nextValue + const roundedValue = precision ? toPrecision(clampedValue, precision) : nextValue + + if (onChange) { + onChange(roundedValue) + } + } + + const handleKeyPress = (event: React.KeyboardEvent) => { + let direction: number + if (event.key === 'ArrowUp') { + direction = 1 + } else if (event.key === 'ArrowDown') { + direction = -1 + } else { + return + } + + event.preventDefault() + handleStep(event, direction) + } + + const handleChange = (event: React.ChangeEvent) => { + const newValue = event.target.value + + const parsedValue = parseFloat(newValue) + + if (!Number.isNaN(parsedValue)) { + const clampedValue = min != null && max != null ? clamp(parsedValue, min, max) : parsedValue + const roundedValue = precision ? toPrecision(clampedValue, precision) : clampedValue + onChange?.(roundedValue) + } + } + + return ( +
+ {prefix} + onRelease?.(value)} + {...rest} + /> + {unit && ( + + {unit} + + )} +
+ ) +} + +NumericInput.defaultProps = { + value: 0, + smallStep: 0.025, + mediumStep: 0.1, + largeStep: 0.25, + min: -Infinity, + max: Infinity, + displayPrecision: 0.001, + precision: Number.EPSILON +} + +export default NumericInput diff --git a/packages/ui/src/components/editor/input/Prefab/index.stories.tsx b/packages/ui/src/components/editor/input/Prefab/index.stories.tsx new file mode 100644 index 00000000000..86fb5703ef9 --- /dev/null +++ b/packages/ui/src/components/editor/input/Prefab/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Prefab', + component: Component, + parameters: { + componentSubtitle: 'PrefabInput', + jest: 'Prefab.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Prefab/index.tsx b/packages/ui/src/components/editor/input/Prefab/index.tsx new file mode 100644 index 00000000000..ca003285442 --- /dev/null +++ b/packages/ui/src/components/editor/input/Prefab/index.tsx @@ -0,0 +1,36 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import FileBrowserInput from '../FileBrowser' +import { StringInputProps } from '../String' + +function PrefabInput({ ...rest }: StringInputProps) { + return +} + +PrefabInput.defaultProps = {} + +export default PrefabInput diff --git a/packages/ui/src/components/editor/input/Progress/index.stories.tsx b/packages/ui/src/components/editor/input/Progress/index.stories.tsx new file mode 100644 index 00000000000..d04714b39bd --- /dev/null +++ b/packages/ui/src/components/editor/input/Progress/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import ProgressBar from './index' +const argTypes = {} + +export default { + title: 'Editor/Input/Progress', + component: ProgressBar, + parameters: { + componentSubtitle: 'ProgressBar', + jest: 'ProgressBar.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: ProgressBar.defaultProps } diff --git a/packages/ui/src/components/editor/input/Progress/index.tsx b/packages/ui/src/components/editor/input/Progress/index.tsx new file mode 100644 index 00000000000..cc4facbe31f --- /dev/null +++ b/packages/ui/src/components/editor/input/Progress/index.tsx @@ -0,0 +1,52 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { HiPause, HiPlay } from 'react-icons/hi2' +import Progress, { ProgressProps } from '../../../../primitives/tailwind/Progress' + +export interface ProgressBarProps extends ProgressProps { + paused: boolean + totalTime: number +} + +export default function ProgressBar({ value, paused, totalTime, ...rest }: ProgressBarProps) { + return ( +
+ {paused ? : } + +
+ {paused + ? 'Paused' + : `${Math.floor((totalTime * value) / 100 / 60)}:${Math.floor( + ((totalTime * value) / 100) % 60 + )} / ${Math.floor(totalTime / 60)}:${Math.floor(totalTime % 60)} `} +
+
+ ) +} + +ProgressBar.defaultProps = {} diff --git a/packages/ui/src/components/editor/input/Select/index.stories.tsx b/packages/ui/src/components/editor/input/Select/index.stories.tsx new file mode 100644 index 00000000000..a2d11cb3ac6 --- /dev/null +++ b/packages/ui/src/components/editor/input/Select/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import SelectInput from './index' +const argTypes = {} + +export default { + title: 'Editor/Input/Select', + component: SelectInput, + parameters: { + componentSubtitle: 'SelectInput', + jest: 'Select.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: SelectInput.defaultProps } diff --git a/packages/ui/src/components/editor/input/Select/index.tsx b/packages/ui/src/components/editor/input/Select/index.tsx new file mode 100644 index 00000000000..92955ba538f --- /dev/null +++ b/packages/ui/src/components/editor/input/Select/index.tsx @@ -0,0 +1,56 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { MdOutlineHeatPump, MdOutlineWatch, MdOutlineWindPower } from 'react-icons/md' +import Select, { SelectProps } from '../../../../primitives/tailwind/Select' + +/**Tailwind `Select` styled for studio */ +const SelectInput = ({ + value, + ...rest +}: Omit, 'currentValue'> & { value: string | number }) => { + return ( + { + onChange?.(e.target.value) + }} + onBlur={(e) => { + onRelease?.(e.target.value) + }} + onFocus={(e) => { + onRelease?.(e.target.value) + }} + {...rest} + /> + ) +} + +StringInput.displayName = 'StringInput' +StringInput.defaultProps = { + value: '', + onChange: () => {}, + type: 'text', + required: false, + placeholder: '' +} + +export default StringInput + +// do we really need a controlled string input? we could easily integrate this with string input itself +export const ControlledStringInput = React.forwardRef((values, ref) => { + const { onChange, onRelease, value, placeholder, disabled, type, containerClassname, ...rest } = values + const [tempValue, setTempValue] = useState(value) + + useEffect(() => { + setTempValue(value) + }, [value]) + + const onBlur = () => { + onRelease?.(tempValue) + } + + const onChangeValue = (value: string) => { + setTempValue(value) + onChange?.(value) + } + + return ( + { + onChangeValue(e.target.value) + }} + onBlur={onBlur} + disabled={disabled} + placeholder={placeholder} + type="text" + /> + ) +}) + +ControlledStringInput.displayName = 'ControlledStringInput' + +ControlledStringInput.defaultProps = { + value: '', + onChange: () => {}, + type: 'text' +} diff --git a/packages/ui/src/components/editor/input/Texture/index.stories.tsx b/packages/ui/src/components/editor/input/Texture/index.stories.tsx new file mode 100644 index 00000000000..b8de703ce8f --- /dev/null +++ b/packages/ui/src/components/editor/input/Texture/index.stories.tsx @@ -0,0 +1,48 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import image from '../../../../../../../PoweredByEE.png' +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Texture', + component: Component, + parameters: { + componentSubtitle: 'TextureInput', + jest: 'Texture.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { + args: { + value: image + } +} diff --git a/packages/ui/src/components/editor/input/Texture/index.tsx b/packages/ui/src/components/editor/input/Texture/index.tsx new file mode 100644 index 00000000000..67b7ab55539 --- /dev/null +++ b/packages/ui/src/components/editor/input/Texture/index.tsx @@ -0,0 +1,185 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { Fragment, useEffect } from 'react' +import { ColorSpace, DisplayP3ColorSpace, LinearSRGBColorSpace, SRGBColorSpace, Texture, Vector2 } from 'three' + +import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader' +import { ImageFileTypes, VideoFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import { AssetClass } from '@etherealengine/engine/src/assets/enum/AssetClass' +import { useHookstate } from '@etherealengine/hyperflux' + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import Button from '../../../../primitives/tailwind/Button' +import FileBrowserInput from '../FileBrowser' +import InputGroup from '../Group' +import { ImageContainer } from '../Image/Preview' +import SelectInput from '../Select' +import { StringInputProps } from '../String' +import { Vector2Input } from '../Vector2' + +/** + * VideoInput used to render component view for video inputs. + */ +export function TextureInput({ ...rest }: StringInputProps) { + return ( + + ) +} + +export default function TexturePreviewInput({ + value, + onRelease, + ...rest +}: { + value: string | Texture + onRelease: (value: any) => void + preview?: string +}) { + const { preview } = rest + const validSrcValue = + typeof value === 'string' && [AssetClass.Image, AssetClass.Video].includes(AssetLoader.getAssetClass(value)) + + const srcState = useHookstate(value) + const texture = srcState.value as Texture + const src = srcState.value as string + const showPreview = preview !== undefined || validSrcValue + const previewSrc = validSrcValue ? value : preview + const inputSrc = validSrcValue + ? value + : texture?.isTexture + ? texture.source?.data?.src ?? texture?.userData?.src ?? (preview ? 'BLOB' : '') + : src + const offset = useHookstate(typeof texture?.offset?.clone === 'function' ? texture.offset.clone() : new Vector2(0, 0)) + const scale = useHookstate(typeof texture?.repeat?.clone === 'function' ? texture.repeat.clone() : new Vector2(1, 1)) + const colorspace = useHookstate( + texture?.colorSpace ? texture?.colorSpace : (new String(LinearSRGBColorSpace) as ColorSpace) + ) + + useEffect(() => { + if (texture?.isTexture && !texture.isRenderTargetTexture) { + offset.set(texture.offset) + scale.set(texture.repeat) + colorspace.set(texture.colorSpace) + } + }, [srcState]) + + return ( + +
+ {showPreview && ( +
+
+
+ + {(typeof preview === 'string' || + (typeof value === 'string' && AssetLoader.getAssetClass(value) === AssetClass.Image)) && ( + + )} + {typeof value === 'string' && AssetLoader.getAssetClass(value) === AssetClass.Video && ( + +
+
+
+ )} +
+ +
+ {texture?.isTexture && !texture.isRenderTargetTexture && ( + <> + { + offset.set(_offset) + texture.offset.copy(_offset) + }} + uniformScaling={false} + /> + { + scale.set(_scale) + texture.repeat.copy(_scale) + }} + uniformScaling={false} + /> + + )} + {texture?.isTexture && ( + <> + + { + colorspace.set(value) + texture.colorSpace = value + texture.needsUpdate = true + console.log('DEBUG changed space', texture.colorSpace) + }} + /> + + + )} + {value && ( + <> +
+ +
+ + )} +
+
+ ) +} + +export function TexturePreviewInputGroup({ name, label, value, onRelease, ...rest }) { + return ( + + + + ) +} diff --git a/packages/ui/src/components/editor/input/Vector2/index.stories.tsx b/packages/ui/src/components/editor/input/Vector2/index.stories.tsx new file mode 100644 index 00000000000..98bc69a6d93 --- /dev/null +++ b/packages/ui/src/components/editor/input/Vector2/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Vector2', + component: Component, + parameters: { + componentSubtitle: 'Vector2Input', + jest: 'Vector2.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Vector2/index.tsx b/packages/ui/src/components/editor/input/Vector2/index.tsx new file mode 100644 index 00000000000..b48d4707198 --- /dev/null +++ b/packages/ui/src/components/editor/input/Vector2/index.tsx @@ -0,0 +1,123 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { useHookstate } from '@etherealengine/hyperflux' +import React from 'react' +import { Vector2 } from 'three' + +import { Vector2_Zero } from '@etherealengine/spatial/src/common/constants/MathConstants' +import NumericInput from '../Numeric' +import { Vector3Scrubber } from '../Vector3' + +interface Vector2InputProp { + uniformScaling?: boolean + smallStep?: number + mediumStep?: number + largeStep?: number + value: Vector2 + hideLabels?: boolean + onChange: (v: Vector2) => void + onRelease?: (v: Vector2) => void +} + +export const Vector2Input = ({ + uniformScaling, + smallStep, + mediumStep, + largeStep, + value, + hideLabels, + onChange, + onRelease, + ...rest +}: Vector2InputProp) => { + const uniformEnabled = useHookstate(uniformScaling) + + const processChange = (field: string, fieldValue: number) => { + if (uniformEnabled.value) { + value.set(fieldValue, fieldValue) + } else { + value[field] = fieldValue + } + } + + const onChangeX = (x: number) => { + processChange('x', x) + onChange(value) + } + + const onChangeY = (y: number) => { + processChange('y', y) + onChange(value) + } + + const onReleaseX = (x: number) => { + processChange('x', x) + onRelease?.(value) + } + + const onReleaseY = (y: number) => { + processChange('y', y) + onRelease?.(value) + } + + const vx = value.x + const vy = value.y + + return ( +
+ + ) + } + /> + + ) + } + /> +
+ ) +} + +Vector2Input.defaultProps = { + value: Vector2_Zero, + hideLabels: false, + onChange: () => {} +} + +export default Vector2Input diff --git a/packages/ui/src/components/editor/input/Vector3/index.stories.tsx b/packages/ui/src/components/editor/input/Vector3/index.stories.tsx new file mode 100644 index 00000000000..e5e3a6ca1e8 --- /dev/null +++ b/packages/ui/src/components/editor/input/Vector3/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Input/Vector3', + component: Component, + parameters: { + componentSubtitle: 'Vector3Input', + jest: 'Vector3.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/input/Vector3/index.tsx b/packages/ui/src/components/editor/input/Vector3/index.tsx new file mode 100644 index 00000000000..078395e1778 --- /dev/null +++ b/packages/ui/src/components/editor/input/Vector3/index.tsx @@ -0,0 +1,164 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { useHookstate } from '@etherealengine/hyperflux' +import { Vector3_Zero } from '@etherealengine/spatial/src/common/constants/MathConstants' +import React from 'react' +import { twMerge } from 'tailwind-merge' +import { Vector3 } from 'three' +import Scrubber from '../../layout/Scrubber' +import NumericInput from '../Numeric' + +interface Vector3ScrubberProps { + axis?: 'x' | 'y' | 'z' | string + value: number + onChange: any + onPointerUp?: any + children?: any + className?: string +} + +export const Vector3Scrubber = ({ axis, onChange, value, children, ...props }: Vector3ScrubberProps) => { + const color = (() => { + switch (axis) { + case 'x': + return 'red-500' + case 'y': + return 'green-400' // must be fushsia-400 , but these colors doesnt show up + case 'z': + return 'blue-400' //must be teal-400 , but this color doesnt show up + default: + return 'inherit' + } + })() + + props.className = twMerge(`text-${color}`) + const content = children ?? axis?.toUpperCase() + return ( + + {content} + + ) +} + +export const UniformButtonContainer: React.FC<{ children?: any }> = ({ children }) => { + return ( +
+ {children} +
+ ) +} + +interface Vector3InputProp { + uniformScaling?: boolean + smallStep?: number + mediumStep?: number + largeStep?: number + value: Vector3 + hideLabels?: boolean + onChange: (v: Vector3) => void + onRelease?: (v: Vector3) => void +} + +export const Vector3Input = ({ + uniformScaling, + smallStep, + mediumStep, + largeStep, + value, + hideLabels, + onChange, + onRelease, + ...rest +}: Vector3InputProp) => { + const uniformEnabled = useHookstate(uniformScaling) + const processChange = (field: string, fieldValue: number) => { + if (uniformEnabled.value) { + value.set(fieldValue, fieldValue, fieldValue) + } else { + value[field] = fieldValue + } + } + + const onChangeAxis = (axis: 'x' | 'y' | 'z') => (axisValue: number) => { + processChange(axis, axisValue) + onChange(value) + } + + const onReleaseAxis = (axis: 'x' | 'y' | 'z') => (axisValue: number) => { + processChange(axis, axisValue) + onRelease?.(value) + } + + const vx = value.x + const vy = value.y + const vz = value.z + + return ( +
+ + ) + } + /> + + ) + } + /> + + ) + } + /> +
+ ) +} + +Vector3Input.defaultProps = { + value: Vector3_Zero, + hideLabels: false, + onChange: () => {} +} + +export default Vector3Input diff --git a/packages/ui/src/components/editor/layout/ClickAwayListener.tsx b/packages/ui/src/components/editor/layout/ClickAwayListener.tsx new file mode 100644 index 00000000000..2763ed987d6 --- /dev/null +++ b/packages/ui/src/components/editor/layout/ClickAwayListener.tsx @@ -0,0 +1,48 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect, useRef } from 'react' + +const ClickAwayListener = ({ onClickAway, children }) => { + const wrapperRef = useRef(null) + + useEffect(() => { + const handleClickOutside = (event) => { + if (wrapperRef.current && !(wrapperRef.current! as HTMLElement).contains(event.target)) { + onClickAway() + } + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [onClickAway]) + + return
{children}
+} + +export default ClickAwayListener diff --git a/packages/ui/src/components/editor/layout/ContextMenu.tsx b/packages/ui/src/components/editor/layout/ContextMenu.tsx new file mode 100644 index 00000000000..8b81a1cfab6 --- /dev/null +++ b/packages/ui/src/components/editor/layout/ContextMenu.tsx @@ -0,0 +1,112 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect, useRef, useState } from 'react' +import { twMerge } from 'tailwind-merge' +import ClickAwayListener from './ClickAwayListener' + +type ContextMenuProps = { + open: boolean + anchorEl: null | HTMLElement + panelId: string + anchorPosition: { left: number; top: number } + onClose: () => void + className?: string +} + +export const ContextMenu = ({ + children, + open, + anchorEl, + panelId, + anchorPosition, + onClose, + className +}: React.PropsWithChildren) => { + const panel = document.getElementById(panelId) + const menuRef = useRef(null) + + // Calculate the Y position of the context menu based on the menu height and space to the bottom of the viewport in order to avoid overflow + const calculatePositionY = () => { + let positionY = open ? anchorPosition.top - panel?.getBoundingClientRect().top! : 0 + + if (open && menuRef.current) { + const menuHeight = menuRef.current.offsetHeight + + // The amount of space that the menu can fill based on the current anchor position + const spaceToBottomFromAnchor = window.innerHeight - anchorPosition.top + // We want to reposition the context menu whenever it will overflow the bottom of the screen + const shouldRepositionMenu = menuHeight > spaceToBottomFromAnchor + + if (shouldRepositionMenu) { + // Align the menu bottom with the bottom of the viewport + positionY = window.innerHeight - menuHeight - (panel?.getBoundingClientRect().top || 0) + 30 + } + } + + return positionY + } + + const positionX = open ? anchorPosition.left - panel?.getBoundingClientRect().left! : 0 + const [positionY, setPositionY] = useState(calculatePositionY()) + + const [isScrollable, setIsScrollable] = useState(false) + const parentRect = panel?.getBoundingClientRect() + + useEffect(() => { + if (open && menuRef.current) { + const menuHeight = menuRef.current.offsetHeight + const parentHeight = parentRect?.height || 0 + + // Make the menu scrollable if it is too tall for the parent component + setIsScrollable(parentHeight <= menuHeight + 1) + + setPositionY(calculatePositionY()) + } + }, [open]) + + return ( + onClose()}> +
+ {open && anchorEl && ( +
+
{children}
+
+ )} +
+
+ ) +} + +export default ContextMenu diff --git a/packages/ui/src/components/editor/layout/Overlay.tsx b/packages/ui/src/components/editor/layout/Overlay.tsx new file mode 100755 index 00000000000..53218d87a3e --- /dev/null +++ b/packages/ui/src/components/editor/layout/Overlay.tsx @@ -0,0 +1,34 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { twMerge } from 'tailwind-merge' + +const Overlay = ({ pointerEvents, children }) => { + const overlayClassName = twMerge('fixed inset-0', `pointer-events-${pointerEvents} ?? 'auto'`) + return
{children}
+} + +export default Overlay diff --git a/packages/ui/src/components/editor/layout/Panel.tsx b/packages/ui/src/components/editor/layout/Panel.tsx new file mode 100755 index 00000000000..04dbd6926f0 --- /dev/null +++ b/packages/ui/src/components/editor/layout/Panel.tsx @@ -0,0 +1,130 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { ReactNode } from 'react' +import Text from '../../../primitives/tailwind/Text' + +const panelIconStyles = { + color: 'var(--textColor)', + marginRight: '6px', + width: '18px' +} + +const panelCheckboxStyles = { + color: 'var(--textColor)', + position: 'relative', + padding: '0px' +} + +const panelContainerStyles = { + position: 'relative', + display: 'flex', + flex: 1, + flexDirection: 'column', + borderRadius: '4px', + backgroundColor: 'var(--dockBackground)', + overflow: 'hidden', + userSelect: 'none' +} + +const panelToolbarStyles = { + display: 'flex', + padding: '4px', + height: '24px', + alignItems: 'center', + borderBottom: '1px solid rgba(0, 0, 0, 0.2)' +} + +const panelContentStyles = { + display: 'flex', + flex: 1, + flexDirection: 'column', + position: 'relative', + overflow: 'hidden' +} + +export const PanelIcon = ({ as: IconComponent, size = 12 }) => { + return +} + +export const PanelTitle = ({ children }) => { + return ( + + {children} + + ) +} + +export const PanelCheckbox = ({ children }) => { + return
{children}
+} + +export const PanelDragContainer = ({ children }) => { + // .dock-tab styled in Editor2Container.css + return
{children}
+} + +export const PanelContainer = ({ children, ...rest }) => { + return ( +
+ {children} +
+ ) +} + +export const PanelToolbar = ({ children }) => { + return ( +
+ {children} +
+ ) +} + +export const PanelContent = ({ children }) => { + return
{children}
+} + +interface PanelProps { + icon?: React.ElementType + title: string + toolbarContent?: React.ReactNode + children?: ReactNode + // Add any other props you want to accept +} + +const Panel: React.FC = ({ icon, title, children, toolbarContent, ...rest }) => { + return ( + + + {icon && } + {title} + {toolbarContent} + + {children} + + ) +} + +export default Panel diff --git a/packages/ui/src/components/editor/layout/Popover.tsx b/packages/ui/src/components/editor/layout/Popover.tsx new file mode 100644 index 00000000000..6cddcaeabe4 --- /dev/null +++ b/packages/ui/src/components/editor/layout/Popover.tsx @@ -0,0 +1,67 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { twMerge } from 'tailwind-merge' +import ClickAwayListener from './ClickAwayListener' + +type ContextMenuProps = { + className?: string + open: boolean + anchorEl: null | HTMLElement + panelId: string + anchorPosition: any + onClose: () => void +} + +export const PopOver = ({ + children, + open, + anchorEl, + panelId, + anchorPosition, + className, + onClose +}: React.PropsWithChildren) => { + const panel = document.getElementById(panelId) + const positionX = anchorPosition?.left - panel?.getBoundingClientRect().left! + const positionY = anchorPosition?.top - panel?.getBoundingClientRect().top! + return ( + onClose()}> +
+ {open && anchorEl && ( +
+ {children} +
+ )} +
+
+ ) +} + +export default PopOver diff --git a/packages/ui/src/components/editor/layout/Scrubber.tsx b/packages/ui/src/components/editor/layout/Scrubber.tsx new file mode 100644 index 00000000000..a106c1ca173 --- /dev/null +++ b/packages/ui/src/components/editor/layout/Scrubber.tsx @@ -0,0 +1,168 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Portal from '@etherealengine/editor/src/components/layout/Portal' +import { getStepSize, toPrecision } from '@etherealengine/editor/src/functions/utils' +import { useHookstate } from '@etherealengine/hyperflux' +import React, { useRef } from 'react' +import { twMerge } from 'tailwind-merge' +import { MathUtils } from 'three' +import Overlay from './Overlay' + +type ScrubberProps = { + className?: string + children?: React.ReactNode + smallStep?: number + mediumStep?: number + largeStep?: number + sensitivity?: number + min?: number + max?: number + precision?: number + convertFrom?: any + convertTo?: any + value?: any + onChange: (value: any) => void + onRelease?: (value: any) => void +} + +const Scrubber: React.FC = ({ + className, + children, + smallStep, + mediumStep, + largeStep, + sensitivity, + min, + max, + precision, + convertFrom, + convertTo, + value, + onChange, + onRelease, + ...rest +}) => { + const containerClassName = twMerge( + 'flex items-center', + 'cursor-ew-resize p-1', + "font-['Figtree'] text-xs font-normal", + className + ) + + const state = useHookstate({ + isDragging: false, + startValue: 0, + delta: 0, + mouseX: 0, + mouseY: 0, + currentValue: 0 + }) + + const scrubberEl = useRef(null) + + const handleMouseMove = (event: React.MouseEvent) => { + if (state.isDragging.value) { + const mX = state.mouseX.value + event.movementX + const mY = state.mouseY.value + event.movementY + const nextDelta = state.delta.value + event.movementX + const stepSize = getStepSize(event, smallStep, mediumStep, largeStep) + const nextValue = (state.startValue.value as number) + Math.round(nextDelta / (sensitivity || 1)) * stepSize + const clampedValue = MathUtils.clamp(nextValue, min ?? -Infinity, max ?? Infinity) + const roundedValue = precision ? toPrecision(clampedValue, precision) : clampedValue + const finalValue = convertTo(roundedValue) + onChange(finalValue) + + state.merge({ + currentValue: finalValue, + delta: nextDelta, + mouseX: mX, + mouseY: mY + }) + } + } + + const handleMouseUp = () => { + if (state.isDragging.value) { + state.merge({ + isDragging: false, + startValue: 0, + delta: 0, + mouseX: 0, + mouseY: 0 + }) + + if (onRelease) { + onRelease(state.currentValue.value) + } + + document.exitPointerLock() + } + } + + const handleMouseDown = (event: React.MouseEvent) => { + state.merge({ + isDragging: true, + startValue: convertFrom(value), + delta: 0, + mouseX: event.clientX, + mouseY: event.clientY + }) + scrubberEl?.current?.requestPointerLock() + } + + return ( +
+ {children} + {state.isDragging.value && ( + + +
+ + + )} +
+ ) +} + +Scrubber.defaultProps = { + smallStep: 0.025, + mediumStep: 0.1, + largeStep: 0.25, + sensitivity: 5, + min: -Infinity, + max: Infinity, + convertFrom: (value) => value, + convertTo: (value) => value +} + +export default React.memo(Scrubber) diff --git a/packages/ui/src/components/editor/layout/Tooltip.tsx b/packages/ui/src/components/editor/layout/Tooltip.tsx new file mode 100755 index 00000000000..873a4dacd26 --- /dev/null +++ b/packages/ui/src/components/editor/layout/Tooltip.tsx @@ -0,0 +1,67 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { Tooltip, TooltipProps } from '@mui/material' +import createStyles from '@mui/styles/createStyles' +import makeStyles from '@mui/styles/makeStyles' + +const useStyles = makeStyles((theme: any) => { + return createStyles({ + tooltip: { + background: theme.panel + } + }) +}) + +/** + * @param {Object} props + * @param {string} props.info additional info added to the tooltip label + */ +export function InfoTooltip(props: TooltipProps & { info?: string }) { + if (!props.title) return <>{props.children} + + const title = props.info ? ( +

+ {props.title} +


+ {props.info} +

+ ) : ( + props.title + ) + + const styles = useStyles({}) + + return ( + + {/* Span is required to trigger events like hover in safari for disabled elements */} + {props.children} + + ) +} + +export default Tooltip diff --git a/packages/ui/src/components/editor/panels/Assets/container/index.tsx b/packages/ui/src/components/editor/panels/Assets/container/index.tsx new file mode 100644 index 00000000000..3c5ad4f2577 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Assets/container/index.tsx @@ -0,0 +1,286 @@ +/* eslint-disable no-case-declarations */ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import { debounce } from 'lodash' +import React, { createContext, useContext, useEffect, useRef } from 'react' +import { useDrag } from 'react-dnd' +import { getEmptyImage } from 'react-dnd-html5-backend' +import { useTranslation } from 'react-i18next' + +import { staticResourcePath, StaticResourceType } from '@etherealengine/common/src/schema.type.module' +import { Engine } from '@etherealengine/ecs/src/Engine' +import { AssetsPanelCategories } from '@etherealengine/editor/src/components/assets/AssetsPanelCategories' +import { AssetSelectionChangePropsType } from '@etherealengine/editor/src/components/assets/AssetsPreviewPanel' +import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader' +import { getState, NO_PROXY, State, useHookstate } from '@etherealengine/hyperflux' +import { BiSolidDownArrow, BiSolidRightArrow } from 'react-icons/bi' +import { HiMagnifyingGlass } from 'react-icons/hi2' +import { twMerge } from 'tailwind-merge' +import Button from '../../../../../primitives/tailwind/Button' +import Input from '../../../../../primitives/tailwind/Input' +import LoadingView from '../../../../../primitives/tailwind/LoadingView' +import Text from '../../../../../primitives/tailwind/Text' +import { FileIcon } from '../../Files/icon' + +type FolderType = { folderType: 'folder'; assetClass: string } +type ResourceType = { folderType: 'staticResource' } & StaticResourceType + +type CategorizedStaticResourceType = FolderType | ResourceType + +const AssetsPreviewContext = createContext({ onAssetSelectionChanged: (props: AssetSelectionChangePropsType) => {} }) + +const ResourceFile = ({ resource }: { resource: StaticResourceType }) => { + const { onAssetSelectionChanged } = useContext(AssetsPreviewContext) + + const assetType = AssetLoader.getAssetType(resource.key) + const [_, drag, preview] = useDrag(() => ({ + type: assetType, + item: { + url: resource.url + }, + multiple: false + })) + + useEffect(() => { + if (preview) preview(getEmptyImage(), { captureDraggingState: true }) + }, [preview]) + + const fullName = resource.key.split('/').at(-1)! + const name = fullName.length > 15 ? `${fullName.substring(0, 12)}...` : fullName + + return ( +
+ onAssetSelectionChanged?.({ + contentType: assetType, + name: fullName, + resourceUrl: resource.url, + size: 'unknown size' + }) + } + className="mt-[10px] flex cursor-pointer flex-col items-center justify-center align-middle" + > + + + + {name} +
+ ) +} + +type Category = { + name: string + object: object + collapsed: boolean + isLeaf: boolean + depth: number +} + +function iterativelyListTags(obj: object): string[] { + const tags: string[] = [] + for (const key in obj) { + tags.push(key) + if (typeof obj[key] === 'object') { + tags.push(...iterativelyListTags(obj[key])) + } + } + return tags +} + +const AssetCategory = (props: { + data: { + categories: Category[] + onClick: (resource: Category) => void + selectedCategory: Category | null + collapsedCategories: State<{ [key: string]: boolean }> + } + index: number +}) => { + const { categories, onClick, selectedCategory, collapsedCategories } = props.data + const index = props.index + const resource = categories[index] + + return ( +
+ + {!item.isFolder && ( + + )} + {!item.isFolder && ( + + )} + {!item.isFolder && ( + + )} + + + + + + + + + + + + ) +} diff --git a/packages/ui/src/components/editor/panels/Files/container/index.stories.tsx b/packages/ui/src/components/editor/panels/Files/container/index.stories.tsx new file mode 100644 index 00000000000..b37f34cdd05 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/container/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Files/Container', + component: Component, + parameters: { + componentSubtitle: 'FilesPanel', + jest: 'FilesPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Files/container/index.tsx b/packages/ui/src/components/editor/panels/Files/container/index.tsx new file mode 100644 index 00000000000..6e8382816a4 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/container/index.tsx @@ -0,0 +1,645 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { FileThumbnailJobState } from '@etherealengine/client-core/src/common/services/FileThumbnailJobState' +import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService' +import { uploadToFeathersService } from '@etherealengine/client-core/src/util/upload' +import config from '@etherealengine/common/src/config' +import { + FileBrowserContentType, + archiverPath, + fileBrowserPath, + fileBrowserUploadPath, + staticResourcePath +} from '@etherealengine/common/src/schema.type.module' +import { CommonKnownContentTypes } from '@etherealengine/common/src/utils/CommonKnownContentTypes' +import { processFileName } from '@etherealengine/common/src/utils/processFileName' +import { Engine } from '@etherealengine/ecs' +import { AssetSelectionChangePropsType } from '@etherealengine/editor/src/components/assets/AssetsPreviewPanel' +import { + FilesViewModeSettings, + FilesViewModeState, + availableTableColumns +} from '@etherealengine/editor/src/components/assets/FileBrowser/FileBrowserState' +import { FileDataType } from '@etherealengine/editor/src/components/assets/FileBrowser/FileDataType' +import { DndWrapper } from '@etherealengine/editor/src/components/dnd/DndWrapper' +import { SupportedFileTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { downloadBlobAsZip, inputFileWithAddToScene } from '@etherealengine/editor/src/functions/assetFunctions' +import { bytesToSize, unique } from '@etherealengine/editor/src/functions/utils' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { AssetLoader } from '@etherealengine/engine/src/assets/classes/AssetLoader' +import { + ImageConvertDefaultParms, + ImageConvertParms +} from '@etherealengine/engine/src/assets/constants/ImageConvertParms' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { useFind, useMutation, useSearch } from '@etherealengine/spatial/src/common/functions/FeathersHooks' +import React, { useEffect, useRef } from 'react' +import { useDrop } from 'react-dnd' +import { useTranslation } from 'react-i18next' +import { FaList } from 'react-icons/fa' +import { FiDownload, FiGrid, FiRefreshCcw } from 'react-icons/fi' +import { HiOutlinePlusCircle } from 'react-icons/hi' +import { HiMagnifyingGlass } from 'react-icons/hi2' +import { IoArrowBack, IoSettingsSharp } from 'react-icons/io5' +import { PiFolderPlusBold } from 'react-icons/pi' +import { twMerge } from 'tailwind-merge' +import { FilesPanelTab } from '..' +import Button from '../../../../../primitives/tailwind/Button' +import Input from '../../../../../primitives/tailwind/Input' +import LoadingView from '../../../../../primitives/tailwind/LoadingView' +import Slider from '../../../../../primitives/tailwind/Slider' +import Tooltip from '../../../../../primitives/tailwind/Tooltip' +import BooleanInput from '../../../input/Boolean' +import InputGroup from '../../../input/Group' +import Popover from '../../../layout/Popover' +import { FileBrowserItem, FileTableWrapper, canDropItemOverFolder } from '../browserGrid' + +type FileBrowserContentPanelProps = { + onSelectionChanged: (assetSelectionChange: AssetSelectionChangePropsType) => void + disableDnD?: boolean + selectedFile?: string + folderName?: string + nestingDirectory?: string +} + +type DnDFileType = { + dataTransfer: DataTransfer + files: File[] + items: DataTransferItemList +} + +export const FILES_PAGE_LIMIT = 100 + +export type FileType = { + fullName: string + isFolder: boolean + key: string + name: string + path: string + size: string + type: string + url: string +} + +const fileConsistsOfContentType = function (file: FileType, contentType: string): boolean { + if (file.isFolder) { + return contentType.startsWith('image') + } else { + const guessedType: string = CommonKnownContentTypes[file.type] + return guessedType?.startsWith(contentType) + } +} + +export function isFileDataType(value: any): value is FileDataType { + return value && value.key +} + +/** + * FileBrowserPanel used to render view for AssetsPanel. + */ +const FileBrowserContentPanel: React.FC = (props) => { + const { t } = useTranslation() + + const originalPath = `/${props.folderName || 'projects'}/${props.selectedFile ? props.selectedFile + '/' : ''}` + const selectedDirectory = useHookstate(originalPath) + const nestingDirectory = useHookstate(props.nestingDirectory || 'projects') + const fileProperties = useHookstate(null) + const anchorEl = useHookstate(null) + + const openProperties = useHookstate(false) + const openCompress = useHookstate(false) + const openConvert = useHookstate(false) + const convertProperties = useHookstate(ImageConvertDefaultParms) + + const openConfirm = useHookstate(false) + const contentToDeletePath = useHookstate('') + + const filesViewMode = useHookstate(getMutableState(FilesViewModeState).viewMode) + const [anchorPosition, setAnchorPosition] = React.useState(undefined) + + const page = useHookstate(0) + + const fileQuery = useFind(fileBrowserPath, { + query: { + $skip: page.value, + $limit: FILES_PAGE_LIMIT * 100, + directory: selectedDirectory.value + } + }) + + const searchText = useHookstate('') + + useSearch( + fileQuery, + { + key: { + $like: `%${searchText.value}%` + } + }, + searchText.value + ) + + const fileService = useMutation(fileBrowserPath) + + const isLoading = fileQuery.status === 'pending' + + const files = fileQuery.data.map((file: FileBrowserContentType) => { + const isFolder = file.type === 'folder' + const fullName = isFolder ? file.name : file.name + '.' + file.type + + return { + ...file, + size: file.size ? bytesToSize(file.size) : '0', + path: isFolder ? file.key.split(file.name)[0] : file.key.split(fullName)[0], + fullName, + isFolder + } + }) + + useEffect(() => { + FileThumbnailJobState.processFiles(fileQuery.data as FileBrowserContentType[]) + }, [fileQuery.data]) + + useEffect(() => { + refreshDirectory() + }, [selectedDirectory]) + + const refreshDirectory = async () => { + fileQuery.refetch() + } + + const changeDirectoryByPath = (path: string) => { + selectedDirectory.set(path) + fileQuery.setPage(0) + } + + const onSelect = (params: FileDataType) => { + if (params.type !== 'folder') { + props.onSelectionChanged({ + resourceUrl: params.url, + name: params.name, + contentType: params.type, + size: params.size + }) + } else { + const newPath = `${selectedDirectory.value}${params.name}/` + changeDirectoryByPath(newPath) + } + } + + const handlePageChange = async (_event, newPage: number) => { + page.set(newPage) + } + + const createNewFolder = async () => { + fileService.create(`${selectedDirectory.value}New_Folder`) + } + + const dropItemsOnPanel = async (data: FileDataType | DnDFileType, dropOn?: FileDataType) => { + if (isLoading) return + + const path = dropOn?.isFolder ? dropOn.key : selectedDirectory.value + + if (isFileDataType(data)) { + if (dropOn?.isFolder) { + moveContent(data.fullName, data.fullName, data.path, path, false) + } + } else { + await Promise.all( + data.files.map(async (file) => { + const assetType = !file.type ? AssetLoader.getAssetType(file.name) : file.type + if (!assetType) { + // file is directory + fileService.create(`${path}${file.name}`) + } else { + try { + const name = processFileName(file.name) + await uploadToFeathersService(fileBrowserUploadPath, [file], { + fileName: name, + path, + contentType: file.type + }).promise + } catch (err) { + NotificationService.dispatchNotify(err.message, { variant: 'error' }) + } + } + }) + ) + } + + await refreshDirectory() + } + + const onBackDirectory = () => { + const pattern = /([^/]+)/g + const result = selectedDirectory.value.match(pattern) + if (!result || result.length === 1) return + let newPath = '/' + for (let i = 0; i < result.length - 1; i++) { + newPath += result[i] + '/' + } + changeDirectoryByPath(newPath) + } + + const moveContent = async ( + oldName: string, + newName: string, + oldPath: string, + newPath: string, + isCopy = false + ): Promise => { + if (isLoading) return + fileService.update(null, { oldName, newName, oldPath, newPath, isCopy }) + } + + const handleConfirmDelete = (contentPath: string, type: string) => { + contentToDeletePath.set(contentPath) + + openConfirm.set(true) + } + + const handleConfirmClose = () => { + contentToDeletePath.set('') + + openConfirm.set(false) + } + + const deleteContent = async (): Promise => { + if (isLoading) return + openConfirm.set(false) + fileService.remove(contentToDeletePath.value) + props.onSelectionChanged({ resourceUrl: '', name: '', contentType: '', size: '' }) + } + + const currentContentRef = useRef(null! as { item: FileDataType; isCopy: boolean }) + + const showUploadAndDownloadButtons = + selectedDirectory.value.slice(1).startsWith('projects/') && + !['projects', 'projects/'].includes(selectedDirectory.value.slice(1)) + const showBackButton = selectedDirectory.value.split('/').length > originalPath.split('/').length + + const handleDownloadProject = async () => { + const url = selectedDirectory.value + const data = await Engine.instance.api + .service(archiverPath) + .get(null, { query: { directory: url } }) + .catch((err: Error) => { + NotificationService.dispatchNotify(err.message, { variant: 'warning' }) + return null + }) + if (!data) return + const blob = await (await fetch(`${config.client.fileServer}/${data}`)).blob() + + let fileName: string + if (selectedDirectory.value[selectedDirectory.value.length - 1] === '/') { + fileName = selectedDirectory.value.split('/').at(-2) as string + } else { + fileName = selectedDirectory.value.split('/').at(-1) as string + } + + downloadBlobAsZip(blob, fileName) + } + + const BreadcrumbItems = () => { + const handleBreadcrumbDirectoryClick = (targetFolder: string) => { + const pattern = /([^/]+)/g + const result = selectedDirectory.value.match(pattern) + if (!result) return + let newPath = '/' + for (const folder of result) { + newPath += folder + '/' + if (folder === targetFolder) { + break + } + } + changeDirectoryByPath(newPath) + } + let breadcrumbDirectoryFiles = selectedDirectory.value.slice(1, -1).split('/') + + const nestedIndex = breadcrumbDirectoryFiles.indexOf(nestingDirectory.value) + + breadcrumbDirectoryFiles = breadcrumbDirectoryFiles.filter((_, idx) => idx >= nestedIndex) + + return ( + + ) + } + + const DropArea = () => { + const [{ isFileDropOver }, fileDropRef] = useDrop({ + accept: [...SupportedFileTypes], + canDrop: (item: Record) => 'key' in item || canDropItemOverFolder(selectedDirectory.value), + drop: (dropItem) => dropItemsOnPanel(dropItem as any), + collect: (monitor) => ({ isFileDropOver: monitor.canDrop() && monitor.isOver() }) + }) + + const isListView = filesViewMode.value === 'list' + const staticResourceData = useFind(staticResourcePath, { + query: { + key: { + $in: isListView ? files.map((file) => file.key) : [] + }, + $select: ['key', 'updatedAt'] as any, + $limit: FILES_PAGE_LIMIT + } + }) + const staticResourceModifiedDates = useHookstate>({}) + + useEffect(() => { + const modifiedDates: Record = {} + staticResourceData.data.forEach((data) => { + modifiedDates[data.key] = new Date(data.updatedAt).toLocaleString() + }) + staticResourceModifiedDates.set(modifiedDates) + }, [staticResourceData.data]) + + return ( +
+
+ + <> + {unique(files, (file) => file.key).map((file, i) => ( + + ))} + + + {/* + {total > 0 && validFiles.value.length < total && ( + + )}*/} +
+
+ ) + } + + const ViewModeSettings = () => { + const viewModeSettings = useHookstate(getMutableState(FilesViewModeSettings)) + return ( + <> +
+ +
+ { + anchorEl.set(null) + setAnchorPosition(undefined) + }} + panelId={FilesPanelTab.id!} + anchorPosition={anchorPosition} + className="w-45 flex min-w-[300px] flex-col p-2" + > + {filesViewMode.value === 'icons' ? ( + + + + ) : ( + <> + + + + +
+
+ +
+
+ {availableTableColumns.map((column) => ( + + viewModeSettings.list.selectedTableColumns[column].set(value)} + /> + + ))} +
+
+ + )} +
+ + ) + } + + const viewModes = [ + { mode: 'list', icon: }, + { mode: 'icons', icon: } + ] + + return ( + <> +
+
+ +
+ +
+ +
+ + + +
+ {viewModes.map(({ mode, icon }) => ( +
+ +
+
+ +
+ { + searchText.set(e.target.value) + }} + labelClassname="text-sm text-red-500" + containerClassname="flex h-full bg-theme-primary rounded-[4px] w-full" + className="h-7 w-full rounded-[4px] bg-theme-primary py-0 text-xs text-[#A3A3A3] placeholder:text-[#A3A3A3] focus-visible:ring-0" + startComponent={} + /> +
+ +
+ +
+ +
+ +
+ + +
+ {isLoading && } +
+ + + +
+ + ) +} + +export default function FilesPanelContainer() { + const assetsPreviewPanelRef = React.useRef() + const projectName = useHookstate(getMutableState(EditorState).projectName).value + + const onSelectionChanged = (props: AssetSelectionChangePropsType) => { + ;(assetsPreviewPanelRef as any).current?.onSelectionChanged?.(props) + } + + return ( + <> + + + ) +} diff --git a/packages/ui/src/components/editor/panels/Files/icon/index.tsx b/packages/ui/src/components/editor/panels/Files/icon/index.tsx new file mode 100644 index 00000000000..95034f90363 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/icon/index.tsx @@ -0,0 +1,95 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +//import styles from '../styles.module.scss' +import { HiFolder } from 'react-icons/hi2' +import { IoAccessibilityOutline } from 'react-icons/io5' + +import { MdOutlineAudioFile, MdOutlinePhotoSizeSelectActual, MdOutlineViewInAr } from 'react-icons/md' +import { PiVideoCameraBold } from 'react-icons/pi' +import { TbFileDescription } from 'react-icons/tb' + +const FileIconType = { + gltf: MdOutlineViewInAr, + 'gltf-binary': MdOutlineViewInAr, + glb: MdOutlineViewInAr, + vrm: IoAccessibilityOutline, + usdz: MdOutlineViewInAr, + fbx: MdOutlineViewInAr, + png: MdOutlinePhotoSizeSelectActual, + jpeg: MdOutlinePhotoSizeSelectActual, + jpg: MdOutlinePhotoSizeSelectActual, + ktx2: MdOutlinePhotoSizeSelectActual, + m3u8: PiVideoCameraBold, + mp4: PiVideoCameraBold, + mpeg: MdOutlineAudioFile, + mp3: MdOutlineAudioFile, + 'model/gltf-binary': MdOutlineViewInAr, + 'model/gltf': MdOutlineViewInAr, + 'model/glb': MdOutlineViewInAr, + 'model/vrm': IoAccessibilityOutline, + 'model/usdz': MdOutlineViewInAr, + 'model/fbx': MdOutlineViewInAr, + 'image/png': MdOutlinePhotoSizeSelectActual, + 'image/jpeg': MdOutlinePhotoSizeSelectActual, + 'image/jpg': MdOutlinePhotoSizeSelectActual, + 'application/pdf': null, + 'application/vnd.apple.mpegurl': PiVideoCameraBold, + 'video/mp4': PiVideoCameraBold, + 'audio/mpeg': MdOutlineAudioFile, + 'audio/mp3': MdOutlineAudioFile +} + +export const FileIcon = ({ + thumbnailURL, + type, + isFolder, + color = 'text-white' +}: { + thumbnailURL: string | null + type: string + isFolder?: boolean + color?: string +}) => { + const FallbackIcon = FileIconType[type ?? ''] + + return ( + <> + {isFolder ? ( + + ) : thumbnailURL != null ? ( + + ) : FallbackIcon ? ( + + ) : ( + <> + + {/* type && type.length > 0 && showRibbon && {type} */} + + )} + + ) +} diff --git a/packages/ui/src/components/editor/panels/Files/index.stories.tsx b/packages/ui/src/components/editor/panels/Files/index.stories.tsx new file mode 100644 index 00000000000..dab83434895 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Files', + component: Component, + parameters: { + componentSubtitle: 'FilesPanelTitle', + jest: 'FilesPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Files/index.tsx b/packages/ui/src/components/editor/panels/Files/index.tsx new file mode 100644 index 00000000000..fefd67a1f2c --- /dev/null +++ b/packages/ui/src/components/editor/panels/Files/index.tsx @@ -0,0 +1,54 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import FilesPanelContainer from './container' + +export const FilesPanelTitle = () => { + const { t } = useTranslation() + + return ( +
+ + + {'Files'} + + +
+ ) +} + +export default FilesPanelTitle + +export const FilesPanelTab: TabData = { + id: 'filesPanel', + closable: true, + title: , + content: +} diff --git a/packages/ui/src/components/editor/panels/Hierarchy/container/index.stories.tsx b/packages/ui/src/components/editor/panels/Hierarchy/container/index.stories.tsx new file mode 100644 index 00000000000..663581b1d8a --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/container/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Hierarchy/Container', + component: Component, + parameters: { + componentSubtitle: 'HierarchyPanel', + jest: 'HierarchyPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Hierarchy/container/index.tsx b/packages/ui/src/components/editor/panels/Hierarchy/container/index.tsx new file mode 100644 index 00000000000..4aa53be61da --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/container/index.tsx @@ -0,0 +1,607 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { getComponent, getMutableComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { AllFileTypes } from '@etherealengine/engine/src/assets/constants/fileTypes' +import { getMutableState, getState, none, useHookstate, useMutableState } from '@etherealengine/hyperflux' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { + EntityTreeComponent, + isAncestor, + traverseEntityNode +} from '@etherealengine/spatial/src/transform/components/EntityTree' +import React, { useCallback, useEffect, useState } from 'react' +import { useDrop } from 'react-dnd' +import Hotkeys from 'react-hot-keys' +import { useTranslation } from 'react-i18next' +import AutoSizer from 'react-virtualized-auto-sizer' +import { FixedSizeList } from 'react-window' + +import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService' +import { Engine, EntityUUID, UUIDComponent, entityExists } from '@etherealengine/ecs' +import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' + +import useUpload from '@etherealengine/editor/src/components/assets/useUpload' +import { + HeirarchyTreeNodeType, + heirarchyTreeWalker +} from '@etherealengine/editor/src/components/hierarchy/HeirarchyTreeWalker' +import { ItemTypes, SupportedFileTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { CopyPasteFunctions } from '@etherealengine/editor/src/functions/CopyPasteFunctions' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { addMediaNode } from '@etherealengine/editor/src/functions/addMediaNode' +import { cmdOrCtrlString } from '@etherealengine/editor/src/functions/utils' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { GLTFAssetState, GLTFSnapshotState } from '@etherealengine/engine/src/gltf/GLTFState' +import { HiMagnifyingGlass, HiOutlinePlusCircle } from 'react-icons/hi2' +import Button from '../../../../../primitives/tailwind/Button' +import Input from '../../../../../primitives/tailwind/Input' +import ContextMenu from '../../../layout/ContextMenu' +import HierarchyTreeNode, { HierarchyTreeNodeProps, RenameNodeData, getNodeElId } from '../node' + +const uploadOptions = { + multiple: true, + accepts: AllFileTypes +} + +/** + * HierarchyPanel function component provides view for hierarchy tree. + */ +function HierarchyPanelContents(props: { sceneURL: string; rootEntityUUID: EntityUUID; index: number }) { + const { sceneURL, rootEntityUUID, index } = props + const { t } = useTranslation() + const [contextSelectedItem, setContextSelectedItem] = React.useState(undefined) + const [anchorEl, setAnchorEl] = React.useState(null) + const [anchorPosition, setAnchorPosition] = React.useState({ left: 0, top: 0 }) + const [prevClickedNode, setPrevClickedNode] = useState(null) + const onUpload = useUpload(uploadOptions) + const [renamingNode, setRenamingNode] = useState(null) + const expandedNodes = useHookstate(getMutableState(EditorState).expandedNodes) + const entityHierarchy = useHookstate([]) + const [selectedNode, _setSelectedNode] = useState(null) + const lockPropertiesPanel = useHookstate(getMutableState(EditorState).lockPropertiesPanel) + const searchHierarchy = useHookstate('') + + const rootEntity = UUIDComponent.useEntityByUUID(rootEntityUUID) + const rootEntityTree = useComponent(rootEntity, EntityTreeComponent) + + const MemoTreeNode = useCallback( + (props: HierarchyTreeNodeProps) => ( + + ), + [entityHierarchy] + ) + + const nodeSearch: HeirarchyTreeNodeType[] = [] + if (searchHierarchy.value.length > 0) { + const condition = new RegExp(searchHierarchy.value.toLowerCase()) + entityHierarchy.value.forEach((node) => { + if (node.entity && condition.test(getComponent(node.entity, NameComponent)?.toLowerCase() ?? '')) + nodeSearch.push(node) + }) + } + + useEffect(() => { + if (!expandedNodes.value[sceneURL]) { + expandedNodes.set({ [sceneURL]: { [rootEntity]: true } }) + } + }, []) + + useEffect(() => { + entityHierarchy.set(Array.from(heirarchyTreeWalker(sceneURL, rootEntity))) + }, [expandedNodes, index, rootEntityTree.children]) + + const setSelectedNode = (selection) => !lockPropertiesPanel.value && _setSelectedNode(selection) + + /* Expand & Collapse Functions */ + const expandNode = useCallback( + (node: HeirarchyTreeNodeType) => { + expandedNodes[sceneURL][node.entity].set(true) + }, + [expandedNodes] + ) + + const collapseNode = useCallback( + (node: HeirarchyTreeNodeType) => { + expandedNodes[sceneURL][node.entity].set(none) + }, + [expandedNodes] + ) + + const expandChildren = useCallback( + (node: HeirarchyTreeNodeType) => { + handleClose() + traverseEntityNode(node.entity, (child) => { + expandedNodes[sceneURL][child].set(true) + }) + }, + [expandedNodes] + ) + + const collapseChildren = useCallback( + (node: HeirarchyTreeNodeType) => { + handleClose() + traverseEntityNode(node.entity, (child) => { + expandedNodes[sceneURL][child].set(none) + }) + }, + [expandedNodes] + ) + + /* Event handlers */ + const onMouseDown = useCallback( + (e: MouseEvent, node: HeirarchyTreeNodeType) => { + if (e.detail === 1) { + if (e.ctrlKey) { + EditorControlFunctions.toggleSelection([getComponent(node.entity, UUIDComponent)]) + setSelectedNode(null) + } else if (e.shiftKey && prevClickedNode) { + const startIndex = entityHierarchy.value.findIndex((n) => n.entity === prevClickedNode.entity) + const endIndex = entityHierarchy.value.findIndex((n) => n.entity === node.entity) + const range = entityHierarchy.value.slice(Math.min(startIndex, endIndex), Math.max(startIndex, endIndex) + 1) + const entityUuids = range.filter((n) => n.entity).map((n) => getComponent(n.entity!, UUIDComponent)) + EditorControlFunctions.replaceSelection(entityUuids) + setSelectedNode(node) + } else { + const selected = getState(SelectionState).selectedEntities.includes(getComponent(node.entity, UUIDComponent)) + if (!selected) { + EditorControlFunctions.replaceSelection([getComponent(node.entity, UUIDComponent)]) + setSelectedNode(node) + } + } + setPrevClickedNode(node) + } + }, + [prevClickedNode, entityHierarchy] + ) + + const onContextMenu = (event: React.MouseEvent, item: HeirarchyTreeNodeType) => { + event.preventDefault() + event.stopPropagation() + + setContextSelectedItem(item) + setAnchorEl(event.currentTarget) + setAnchorPosition({ + left: event.clientX + 2, + top: event.clientY - 6 + }) + } + + const handleClose = () => { + setContextSelectedItem(undefined) + setAnchorEl(null) + setAnchorPosition({ left: 0, top: 0 }) + } + + const onClick = useCallback((e: MouseEvent, node: HeirarchyTreeNodeType) => { + if (e.detail === 2) { + const editorCameraState = getMutableComponent(Engine.instance.cameraEntity, CameraOrbitComponent) + editorCameraState.focusedEntities.set([node.entity]) + editorCameraState.refocus.set(true) + } + }, []) + + const onToggle = useCallback( + (_, node: HeirarchyTreeNodeType) => { + if (expandedNodes.value[sceneURL][node.entity]) collapseNode(node) + else expandNode(node) + }, + [expandedNodes, expandNode, collapseNode] + ) + + const onKeyDown = useCallback( + (e: KeyboardEvent, node: HeirarchyTreeNodeType) => { + const nodeIndex = entityHierarchy.value.indexOf(node) + const entityTree = getComponent(node.entity, EntityTreeComponent) + switch (e.key) { + case 'ArrowDown': { + e.preventDefault() + + const nextNode = nodeIndex !== -1 && entityHierarchy.value[nodeIndex + 1] + if (!nextNode) return + + if (e.shiftKey) { + EditorControlFunctions.addToSelection([getComponent(nextNode.entity, UUIDComponent)]) + } + + const nextNodeEl = document.getElementById(getNodeElId(nextNode)) + if (nextNodeEl) { + nextNodeEl.focus() + } + break + } + + case 'ArrowUp': { + e.preventDefault() + + const prevNode = nodeIndex !== -1 && entityHierarchy.value[nodeIndex - 1] + if (!prevNode) return + + if (e.shiftKey) { + EditorControlFunctions.addToSelection([getComponent(prevNode.entity, UUIDComponent)]) + } + + const prevNodeEl = document.getElementById(getNodeElId(prevNode)) + if (prevNodeEl) { + prevNodeEl.focus() + } + break + } + + case 'ArrowLeft': + if (entityTree && (!entityTree.children || entityTree.children.length === 0)) return + + if (e.shiftKey) collapseChildren(node) + else collapseNode(node) + break + + case 'ArrowRight': + if (entityTree && (!entityTree.children || entityTree.children.length === 0)) return + + if (e.shiftKey) expandChildren(node) + else expandNode(node) + break + + case 'Enter': + if (e.shiftKey) { + EditorControlFunctions.toggleSelection([getComponent(node.entity, UUIDComponent)]) + setSelectedNode(null) + } else { + EditorControlFunctions.replaceSelection([getComponent(node.entity, UUIDComponent)]) + setSelectedNode(node) + } + break + + case 'Delete': + case 'Backspace': + if (selectedNode && !renamingNode) onDeleteNode(selectedNode!) + break + } + }, + [entityHierarchy, expandNode, collapseNode, expandChildren, collapseChildren, renamingNode, selectedNode] + ) + + const onDeleteNode = useCallback((node: HeirarchyTreeNodeType) => { + handleClose() + + const selected = getState(SelectionState).selectedEntities.includes(getComponent(node.entity, UUIDComponent)) + const objs = selected ? SelectionState.getSelectedEntities() : [node.entity] + EditorControlFunctions.removeObject(objs) + }, []) + + const onDuplicateNode = useCallback((node: HeirarchyTreeNodeType) => { + handleClose() + + const selected = getState(SelectionState).selectedEntities.includes(getComponent(node.entity, UUIDComponent)) + const objs = selected ? SelectionState.getSelectedEntities() : [node.entity] + EditorControlFunctions.duplicateObject(objs) + }, []) + + const onGroupNodes = useCallback((node: HeirarchyTreeNodeType) => { + handleClose() + + const selected = getState(SelectionState).selectedEntities.includes(getComponent(node.entity, UUIDComponent)) + const objs = selected ? SelectionState.getSelectedEntities() : [node.entity] + + EditorControlFunctions.groupObjects(objs) + }, []) + + const onCopyNode = useCallback((node: HeirarchyTreeNodeType) => { + handleClose() + + const selected = getState(SelectionState).selectedEntities.includes(getComponent(node.entity, UUIDComponent)) + const nodes = selected ? SelectionState.getSelectedEntities() : [node.entity] + CopyPasteFunctions.copyEntities(nodes) + }, []) + + const onPasteNode = useCallback(async (node: HeirarchyTreeNodeType) => { + handleClose() + + CopyPasteFunctions.getPastedEntities() + .then((nodeComponentJSONs) => { + nodeComponentJSONs.forEach((componentJSONs) => { + EditorControlFunctions.createObjectFromSceneElement(componentJSONs, undefined, node.entity) + }) + }) + .catch(() => { + NotificationService.dispatchNotify(t('editor:hierarchy.copy-paste.no-hierarchy-nodes'), { variant: 'error' }) + }) + }, []) + /* Event handlers */ + + /* Rename functions */ + const onRenameNode = useCallback((node: HeirarchyTreeNodeType) => { + handleClose() + + if (node.entity) { + const entity = node.entity + setRenamingNode({ entity, name: getComponent(entity, NameComponent) }) + } else { + // todo + } + }, []) + + const onChangeName = useCallback( + (node: HeirarchyTreeNodeType, name: string) => setRenamingNode({ entity: node.entity, name }), + [] + ) + + const onRenameSubmit = useCallback((node: HeirarchyTreeNodeType, name: string) => { + if (name) { + EditorControlFunctions.modifyName([node.entity], name) + } + + setRenamingNode(null) + }, []) + /* Rename functions */ + + const [, treeContainerDropTarget] = useDrop({ + accept: [ItemTypes.Node, ItemTypes.File, ...SupportedFileTypes], + drop(item: any, monitor) { + if (monitor.didDrop()) return + + // check if item contains files + if (item.files) { + const dndItem: any = monitor.getItem() + const entries = Array.from(dndItem.items).map((item: any) => item.webkitGetAsEntry()) + + //uploading files then adding to editor media + onUpload(entries).then((assets) => { + if (!assets) return + for (const asset of assets) addMediaNode(asset) + }) + + return + } + + if (item.url) { + addMediaNode(item.url) + return + } + + if (item.type === ItemTypes.Component) { + EditorControlFunctions.createObjectFromSceneElement([{ name: item!.componentJsonID }]) + return + } + + EditorControlFunctions.reparentObject(Array.isArray(item.value) ? item.value : [item.value]) + }, + canDrop(item: any, monitor) { + if (!monitor.isOver({ shallow: true })) return false + + // check if item is of node type + if (item.type === ItemTypes.Node) { + const sceneEntity = getState(GLTFAssetState)[sceneURL] + return !(item.multiple + ? item.value.some((otherObject) => isAncestor(otherObject, sceneEntity)) + : isAncestor(item.value, sceneEntity)) + } + + return true + } + }) + + let validNodes = nodeSearch?.length > 0 ? nodeSearch : entityHierarchy.value + validNodes = validNodes.filter((node) => entityExists(node.entity)) + + const HierarchyList = ({ height, width }) => ( + index} + outerRef={treeContainerDropTarget} + innerElementType="ul" + > + {MemoTreeNode} + + ) + + return ( + <> +
+ { + searchHierarchy.set(event.target.value) + }} + className="m-1 rounded bg-theme-primary text-white" + startComponent={} + /> + +
+
+ {HierarchyList} +
+ + + { + e.preventDefault() + e.stopPropagation() + selectedNode && onDuplicateNode(selectedNode!) + }} + > + + + { + e.preventDefault() + e.stopPropagation() + selectedNode && onGroupNodes(selectedNode!) + }} + > + + + { + e.preventDefault() + e.stopPropagation() + selectedNode && onCopyNode(selectedNode) + }} + > + + + { + e.preventDefault() + e.stopPropagation() + selectedNode && onPasteNode(selectedNode) + }} + > + + + + + + + + ) +} + +export default function HierarchyPanel() { + const sceneID = useHookstate(getMutableState(EditorState).scenePath).value + const gltfEntity = useMutableState(EditorState).rootEntity.value + if (!sceneID || !gltfEntity) return null + + const GLTFHierarchySub = () => { + const rootEntityUUID = getComponent(gltfEntity, UUIDComponent) + const sourceID = `${rootEntityUUID}-${sceneID}` + const index = GLTFSnapshotState.useSnapshotIndex(sourceID) + + return ( + + ) + } + + return +} diff --git a/packages/ui/src/components/editor/panels/Hierarchy/index.stories.tsx b/packages/ui/src/components/editor/panels/Hierarchy/index.stories.tsx new file mode 100644 index 00000000000..4310662881a --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Hierarchy', + component: Component, + parameters: { + componentSubtitle: 'HierarchyPanelTitle', + jest: 'HierarchyPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Hierarchy/index.tsx b/packages/ui/src/components/editor/panels/Hierarchy/index.tsx new file mode 100644 index 00000000000..fcd7913228b --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/index.tsx @@ -0,0 +1,51 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import HierarchyPanel from './container' + +export const HierarchyPanelTitle = () => { + const { t } = useTranslation() + + return ( +
+ + {t('editor:hierarchy.lbl')} + +
+ ) +} + +export default HierarchyPanelTitle + +export const HierarchyPanelTab: TabData = { + id: 'hierarchyPanel', + closable: true, + title: , + content: +} diff --git a/packages/ui/src/components/editor/panels/Hierarchy/node/index.stories.tsx b/packages/ui/src/components/editor/panels/Hierarchy/node/index.stories.tsx new file mode 100644 index 00000000000..63e19090d33 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/node/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Hierarchy/Node', + component: Component, + parameters: { + componentSubtitle: 'HierarchyPanelTitle', + jest: 'HierarchyPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Hierarchy/node/index.tsx b/packages/ui/src/components/editor/panels/Hierarchy/node/index.tsx new file mode 100644 index 00000000000..f77a15010da --- /dev/null +++ b/packages/ui/src/components/editor/panels/Hierarchy/node/index.tsx @@ -0,0 +1,381 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { KeyboardEvent, StyleHTMLAttributes, useCallback, useEffect } from 'react' +import { useDrag, useDrop } from 'react-dnd' +import { getEmptyImage } from 'react-dnd-html5-backend' + +import { + getAllComponents, + getComponent, + getOptionalComponent, + hasComponent, + useComponent, + useOptionalComponent +} from '@etherealengine/ecs/src/ComponentFunctions' +import { Entity } from '@etherealengine/ecs/src/Entity' +import { entityExists } from '@etherealengine/ecs/src/EntityFunctions' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { EntityTreeComponent, isAncestor } from '@etherealengine/spatial/src/transform/components/EntityTree' +import { PiEyeBold, PiEyeClosedBold } from 'react-icons/pi' + +import { MdKeyboardArrowDown, MdKeyboardArrowRight } from 'react-icons/md' + +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' + +import { UUIDComponent } from '@etherealengine/ecs' +import useUpload from '@etherealengine/editor/src/components/assets/useUpload' +import { HeirarchyTreeNodeType } from '@etherealengine/editor/src/components/hierarchy/HeirarchyTreeWalker' +import { ItemTypes, SupportedFileTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { addMediaNode } from '@etherealengine/editor/src/functions/addMediaNode' +import { ComponentEditorsState } from '@etherealengine/editor/src/services/ComponentEditors' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { ResourcePendingComponent } from '@etherealengine/engine/src/gltf/ResourcePendingComponent' +import { ErrorComponent } from '@etherealengine/engine/src/scene/components/ErrorComponent' +import { VisibleComponent, setVisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' +import { twMerge } from 'tailwind-merge' +import TransformPropertyGroup from '../../../properties/transform' + +//import styles from './styles.module.scss' + +/** + * getNodeElId function provides id for node. + * + * @param {object} node + * @return {string} + */ +export const getNodeElId = (node: HeirarchyTreeNodeType) => { + return 'hierarchy-node-' + node.entity +} + +export type RenameNodeData = { + entity: Entity + name: string +} + +export type HierarchyTreeNodeData = { + nodes: HeirarchyTreeNodeType[] + renamingNode: RenameNodeData + onToggle: (e: Event, node: HeirarchyTreeNodeType) => void + onKeyDown: (e: Event, node: HeirarchyTreeNodeType) => void + onMouseDown: (e: MouseEvent, node: HeirarchyTreeNodeType) => void + onClick: (e: MouseEvent, node: HeirarchyTreeNodeType) => void + onChangeName: (node: HeirarchyTreeNodeType, name: string) => void + onRenameSubmit: (node: HeirarchyTreeNodeType, name: string) => void + onUpload: ReturnType +} + +export type HierarchyTreeNodeProps = { + index: number + data: HierarchyTreeNodeData + style: StyleHTMLAttributes + onContextMenu: (event: React.MouseEvent, item: HeirarchyTreeNodeType) => void +} + +export const HierarchyTreeNode = (props: HierarchyTreeNodeProps) => { + const node = props.data.nodes[props.index] + const data = props.data + + const uuid = useComponent(node.entity, UUIDComponent) + + const selected = useHookstate(getMutableState(SelectionState).selectedEntities).value.includes(uuid.value) + + const nodeName = useOptionalComponent(node.entity, NameComponent)?.value + + const visible = useOptionalComponent(node.entity, VisibleComponent) + + const errors = useOptionalComponent(node.entity, ErrorComponent) + + const sceneAssetLoading = useOptionalComponent(node.entity, ResourcePendingComponent) + + const toggleVisible = () => { + setVisibleComponent(node.entity, !hasComponent(node.entity, VisibleComponent)) + } + + const onClickToggle = useCallback( + (e: MouseEvent) => { + e.stopPropagation() + if (data.onToggle) data.onToggle(e, node) + }, + [data.onToggle, node] + ) + + const onNodeKeyDown = useCallback( + (e: KeyboardEvent) => { + e.stopPropagation() + if (data.onKeyDown) data.onKeyDown(e as any, node) + }, + [data.onKeyDown, node] + ) + + const onKeyDownNameInput = useCallback( + (e: KeyboardEvent) => { + if (e.key === 'Escape') data.onRenameSubmit(node, null!) + else if (e.key === 'Enter') data.onRenameSubmit(node, (e.target as any).value) + }, + [data.onRenameSubmit, node] + ) + + const onClickNode = useCallback((e) => data.onClick(e, node), [node, data.onClick]) + const onMouseDownNode = useCallback((e) => data.onMouseDown(e, node), [node, data.onMouseDown]) + + const onChangeNodeName = useCallback((e) => data.onChangeName(node, e.target.value), [node, data.onChangeName]) + + const [, drag, preview] = useDrag({ + type: ItemTypes.Node, + item() { + const selectedEntities = SelectionState.getSelectedEntities() + const multiple = selectedEntities.length > 1 + + return { + type: ItemTypes.Node, + multiple, + value: multiple ? selectedEntities : selectedEntities[0] + } + }, + canDrag() { + return !SelectionState.getSelectedEntities().some( + (entity) => !getOptionalComponent(entity, EntityTreeComponent)?.parentEntity + ) + }, + collect: (monitor) => ({ + isDragging: !!monitor.isDragging() + }) + }) + + const dropItem = (node: HeirarchyTreeNodeType, place: 'On' | 'Before' | 'After') => { + let parentNode: Entity | undefined + let beforeNode: Entity + + if (place === 'Before') { + const entityTreeComponent = getOptionalComponent(node.entity, EntityTreeComponent) + parentNode = entityTreeComponent?.parentEntity + beforeNode = node.entity + } else if (place === 'After') { + const entityTreeComponent = getOptionalComponent(node.entity, EntityTreeComponent) + parentNode = entityTreeComponent?.parentEntity + const parentTreeComponent = getOptionalComponent(entityTreeComponent?.parentEntity!, EntityTreeComponent) + if ( + parentTreeComponent && + !node.lastChild && + parentNode && + parentTreeComponent?.children.length > node.childIndex + 1 + ) { + beforeNode = parentTreeComponent.children[node.childIndex + 1] + } + } else { + parentNode = node.entity + } + + if (!parentNode) + return () => { + console.warn('parent is not defined') + } + + return (item: any, monitor): void => { + if (parentNode) { + if (item.files) { + const dndItem: any = monitor.getItem() + const entries = Array.from(dndItem.items).map((item: any) => item.webkitGetAsEntry()) + + //uploading files then adding as media to the editor + data.onUpload(entries).then((assets) => { + if (!assets) return + for (const asset of assets) { + addMediaNode(asset, parentNode, beforeNode) + } + }) + return + } + + if (item.url) { + addMediaNode(item.url, parentNode, beforeNode) + return + } + + if (item.type === ItemTypes.Component) { + EditorControlFunctions.createObjectFromSceneElement([{ name: item!.componentJsonID }], parentNode, beforeNode) + return + } + } + + EditorControlFunctions.reparentObject( + Array.isArray(item.value) ? item.value : [item.value], + beforeNode, + parentNode === null ? undefined : parentNode + ) + } + } + + const canDropItem = (entityNode: Entity, dropOn?: boolean) => { + return (item, monitor): boolean => { + //check if monitor is over or object is not parent element + if (!monitor.isOver()) return false + + if (!dropOn) { + const entityTreeComponent = getComponent(entityNode, EntityTreeComponent) + if (!entityTreeComponent) return false + } + if (item.type === ItemTypes.Node) { + const entityTreeComponent = getComponent(entityNode, EntityTreeComponent) + return ( + (dropOn || !!entityTreeComponent.parentEntity) && + !(item.multiple + ? item.value.some((otherObject) => isAncestor(otherObject, entityNode)) + : isAncestor(item.value, entityNode)) + ) + } + return true + } + } + + const [{ canDropBefore, isOverBefore }, beforeDropTarget] = useDrop({ + accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], + drop: dropItem(node, 'Before'), + canDrop: canDropItem(node.entity), + collect: (monitor) => ({ + canDropBefore: monitor.canDrop(), + isOverBefore: monitor.isOver() + }) + }) + + const [{ canDropAfter, isOverAfter }, afterDropTarget] = useDrop({ + accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], + drop: dropItem(node, 'After'), + canDrop: canDropItem(node.entity), + collect: (monitor) => ({ + canDropAfter: monitor.canDrop(), + isOverAfter: monitor.isOver() + }) + }) + + const [{ canDropOn, isOverOn }, onDropTarget] = useDrop({ + accept: [ItemTypes.Node, ItemTypes.File, ItemTypes.Component, ...SupportedFileTypes], + drop: dropItem(node, 'On'), + canDrop: canDropItem(node.entity, true), + collect: (monitor) => ({ + canDropOn: monitor.canDrop(), + isOverOn: monitor.isOver() + }) + }) + + useEffect(() => { + preview(getEmptyImage(), { captureDraggingState: true }) + }, [preview]) + + const icons = entityExists(node.entity) + ? getAllComponents(node.entity) + .map((c) => getState(ComponentEditorsState)[c.name]?.iconComponent) + .filter((icon) => !!icon) + : [] + const IconComponent = icons.length ? icons[icons.length - 1] : TransformPropertyGroup.iconComponent + const renaming = data.renamingNode && data.renamingNode.entity === node.entity + const marginLeft = node.depth > 0 ? node.depth * 2 + 2 : 0 + + return ( +
  • +
    props.onContextMenu(event, node)} + > +
    +
    + {node.isLeaf ? ( +
    + ) : ( + + )} + +
    + {IconComponent && } +
    + {renaming ? ( +
    + +
    + ) : ( +
    + {nodeName} +
    + )} +
    + +
    +
    + +
    +
    +
  • + ) +} + +export default HierarchyTreeNode diff --git a/packages/ui/src/components/editor/panels/Materials/container/index.tsx b/packages/ui/src/components/editor/panels/Materials/container/index.tsx new file mode 100644 index 00000000000..e654e57b87a --- /dev/null +++ b/packages/ui/src/components/editor/panels/Materials/container/index.tsx @@ -0,0 +1,154 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect } from 'react' +import AutoSizer from 'react-virtualized-auto-sizer' +import { FixedSizeList } from 'react-window' +import { MeshBasicMaterial } from 'three' + +import { pathJoin } from '@etherealengine/common/src/utils/miscUtils' +import { EntityUUID, getComponent, UndefinedEntity, useQuery, UUIDComponent } from '@etherealengine/ecs' +import { ImportSettingsState } from '@etherealengine/editor/src/components/assets/ImportSettingsPanel' +import { uploadProjectFiles } from '@etherealengine/editor/src/functions/assetFunctions' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import exportMaterialsGLTF from '@etherealengine/engine/src/assets/functions/exportMaterialsGLTF' +import { SourceComponent } from '@etherealengine/engine/src/scene/components/SourceComponent' +import { + createMaterialEntity, + getMaterialsFromSource +} from '@etherealengine/engine/src/scene/materials/functions/materialSourcingFunctions' +import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materials/MaterialLibraryState' +import { getMutableState, getState, useHookstate, useState } from '@etherealengine/hyperflux' +import { MaterialComponent, MaterialComponents } from '@etherealengine/spatial/src/renderer/materials/MaterialComponent' +import { useTranslation } from 'react-i18next' +import Button from '../../../../../primitives/tailwind/Button' +import InputGroup from '../../../input/Group' +import StringInput from '../../../input/String' +import { MaterialPreviewPanel } from '../../preview/material' +import MaterialLibraryEntry, { MaterialLibraryEntryType } from '../node' + +export default function MaterialLibraryPanel() { + const { t } = useTranslation() + const srcPath = useState('/mat/material-test') + const materialPreviewPanelRef = React.useRef() + + const materialQuery = useQuery([MaterialComponent[MaterialComponents.State]]) + const nodes = useHookstate([] as MaterialLibraryEntryType[]) + const selected = useHookstate(getMutableState(SelectionState).selectedEntities) + + useEffect(() => { + const materials = selected.value.length + ? getMaterialsFromSource(UUIDComponent.getEntityByUUID(selected.value[0])) + : materialQuery.map((entity) => getComponent(entity, UUIDComponent)) + const result = materials.flatMap((uuid): MaterialLibraryEntryType[] => { + const source = getComponent(UUIDComponent.getEntityByUUID(uuid as EntityUUID), SourceComponent) + return [ + { + uuid: uuid, + path: source + } + ] + }) + nodes.set(result) + }, [materialQuery.length, selected]) + + const onClick = (e: MouseEvent, node: MaterialLibraryEntryType) => { + getMutableState(MaterialSelectionState).selectedMaterial.set(node.uuid) + } + + const MaterialList = ({ height, width }) => ( + index} + innerElementType="ul" + > + {MaterialLibraryEntry} + + ) + + return ( +
    +
    +
    + +
    +
    + + + +
    + + +
    +
    +
    +
    + {MaterialList} +
    +
    + ) +} diff --git a/packages/ui/src/components/editor/panels/Materials/index.stories.tsx b/packages/ui/src/components/editor/panels/Materials/index.stories.tsx new file mode 100644 index 00000000000..a60b28d0961 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Materials/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Materials', + component: Component, + parameters: { + componentSubtitle: 'MaterialsPanelTitle', + jest: 'MaterialsPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Materials/index.tsx b/packages/ui/src/components/editor/panels/Materials/index.tsx new file mode 100644 index 00000000000..2c3d78c857c --- /dev/null +++ b/packages/ui/src/components/editor/panels/Materials/index.tsx @@ -0,0 +1,54 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import MaterialLibraryPanel from './container' + +export const MaterialsPanelTitle = () => { + const { t } = useTranslation() + + return ( +
    + + + {'Materials'} + + +
    + ) +} + +export default MaterialsPanelTitle + +export const MaterialsPanelTab: TabData = { + id: 'materialsPanel', + closable: true, + title: , + content: +} diff --git a/packages/ui/src/components/editor/panels/Materials/node/index.tsx b/packages/ui/src/components/editor/panels/Materials/node/index.tsx new file mode 100644 index 00000000000..cca3fa889fd --- /dev/null +++ b/packages/ui/src/components/editor/panels/Materials/node/index.tsx @@ -0,0 +1,174 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { MouseEvent, StyleHTMLAttributes, useCallback } from 'react' +import { useDrag } from 'react-dnd' + +import { EntityUUID, getOptionalComponent, UUIDComponent } from '@etherealengine/ecs' +import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materials/MaterialLibraryState' +import { getMutableState, useHookstate, useMutableState } from '@etherealengine/hyperflux' +import { MaterialComponent, MaterialComponents } from '@etherealengine/spatial/src/renderer/materials/MaterialComponent' + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { SiRoundcube } from 'react-icons/si' +import { twMerge } from 'tailwind-merge' + +export type MaterialLibraryEntryType = { + uuid: EntityUUID + path: string + selected?: boolean +} + +export type MaterialLibraryEntryData = { + nodes: MaterialLibraryEntryType[] + onClick: (e: MouseEvent, node: MaterialLibraryEntryType) => void + onCollapse: (e: MouseEvent, node: MaterialLibraryEntryType) => void +} + +export type MaterialLibraryEntryProps = { + index: number + data: MaterialLibraryEntryData + style: StyleHTMLAttributes +} + +const nodeDisplayName = (node: MaterialLibraryEntryType) => { + return ( + getOptionalComponent( + UUIDComponent.getEntityByUUID(node.uuid as EntityUUID), + MaterialComponent[MaterialComponents.State] + )?.material?.name ?? '' + ) +} + +export default function MaterialLibraryEntry(props: MaterialLibraryEntryProps) { + const data = props.data + const node = data.nodes[props.index] + + const selectionState = useMutableState(SelectionState) + + const onClickNode = (e) => { + data.onClick(e, node) + } + + const onCollapseNode = useCallback( + (e: MouseEvent) => { + e.stopPropagation() + data.onCollapse(e, node) + }, + [node, data.onCollapse] + ) + + const [_dragProps, drag] = useDrag({ + type: ItemTypes.Material, + item() { + const selectedEntities = selectionState.selectedEntities.value + const multiple = selectedEntities.length > 1 + return { + type: ItemTypes.Material, + multiple, + value: node.uuid + } + }, + collect: (monitor) => ({ + isDragging: !!monitor.isDragging() + }) + }) + + const materialSelection = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial) + return ( +
  • +
    +
    + {/* node.isLeaf ? ( +
    + ) : ( + + )*/} + +
    + +
    +
    + {nodeDisplayName(node)} +
    +
    + {/**/} +
    +
    +
    + + {/*
    + + +
    + +
    +
    +
    + +
    {nodeDisplayName(node)}
    +
    + +
    */} +
  • + ) +} diff --git a/packages/ui/src/components/editor/panels/Properties/container/index.stories.tsx b/packages/ui/src/components/editor/panels/Properties/container/index.stories.tsx new file mode 100644 index 00000000000..691b38b2942 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/container/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Properties/Container', + component: Component, + parameters: { + componentSubtitle: 'PropertiesPanelContainer', + jest: 'PropertiesPanelContainer.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Properties/container/index.tsx b/packages/ui/src/components/editor/panels/Properties/container/index.tsx new file mode 100644 index 00000000000..1fdec5cc4f2 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/container/index.tsx @@ -0,0 +1,154 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { UUIDComponent } from '@etherealengine/ecs' +import { Component, ComponentJSONIDMap, useOptionalComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { NO_PROXY, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' + +import { EntityUUID } from '@etherealengine/ecs' +import { ComponentEditorsState } from '@etherealengine/editor/src/services/ComponentEditors' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { GLTFNodeState } from '@etherealengine/engine/src/gltf/GLTFDocumentState' +import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materials/MaterialLibraryState' +import { PopoverPosition } from '@mui/material' +import { HiOutlinePlusCircle } from 'react-icons/hi' +import { PropertiesPanelTab } from '..' +import Button from '../../../../../primitives/tailwind/Button' +import Popover from '../../../layout/Popover' +import TransformPropertyGroup from '../../../properties/transform' +import { PopoverContext } from '../../../util/PopoverContext' +import ElementList from '../elementList' +import MaterialEditor from '../material' + +const EntityComponentEditor = (props: { entity; component; multiEdit }) => { + const { entity, component, multiEdit } = props + const componentMounted = useOptionalComponent(entity, component) + const Editor = getState(ComponentEditorsState)[component.name]! + if (!componentMounted) return null + // nodeEntity is used as key here to signal to React when the entity has changed, + // and to prevent state from being recycled between editor instances, which + // can cause hookstate to throw errors. + return +} + +const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => { + const { t } = useTranslation() + const { entityUUID, multiEdit } = props + const anchorEl = useHookstate(null) + const [anchorPosition, setAnchorPosition] = React.useState(undefined) + + const entity = UUIDComponent.getEntityByUUID(entityUUID) + const componentEditors = useHookstate(getMutableState(ComponentEditorsState)).get(NO_PROXY) + const node = useHookstate(GLTFNodeState.getMutableNode(entity)) + const components: Component[] = [] + for (const jsonID of Object.keys(node.extensions.value!)) { + const component = ComponentJSONIDMap.get(jsonID)! + if (!componentEditors[component.name]) continue + components.push(component) + } + + const open = !!anchorEl.value + const panel = document.getElementById('propertiesPanel') + + return ( + { + anchorEl.set(null) + } + }} + > +
    + +
    + { + anchorEl.set(null) + setAnchorPosition(undefined) + }} + panelId={PropertiesPanelTab.id!} + anchorPosition={anchorPosition} + className="h-[60%] w-full min-w-[300px] overflow-y-auto" + > + {} + + + {components.map((c, i) => ( + + ))} +
    + ) +} + +/** + * PropertiesPanelContainer used to render editor view to customize property of selected element. + */ +export const PropertiesPanelContainer = () => { + const selectedEntities = useHookstate(getMutableState(SelectionState).selectedEntities).value + const lockedNode = useHookstate(getMutableState(EditorState).lockPropertiesPanel) + const multiEdit = selectedEntities.length > 1 + const uuid = lockedNode.value ? lockedNode.value : selectedEntities[selectedEntities.length - 1] + const { t } = useTranslation() + const materialUUID = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial).value + + return ( +
    + {materialUUID ? ( + + ) : uuid ? ( + + ) : ( +
    + {t('editor:properties.noNodeSelected')} +
    + )} +
    + ) +} + +export default PropertiesPanelContainer diff --git a/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx b/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx new file mode 100644 index 00000000000..9cbb57c3c77 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx @@ -0,0 +1,188 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { startCase } from 'lodash' +import React, { useRef } from 'react' +import { useTranslation } from 'react-i18next' + +import { Component } from '@etherealengine/ecs/src/ComponentFunctions' +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' + +import PlaceHolderIcon from '@mui/icons-material/GroupAddOutlined' + +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { ComponentEditorsState } from '@etherealengine/editor/src/services/ComponentEditors' +import { ComponentShelfCategoriesState } from '@etherealengine/editor/src/services/ComponentShelfCategoriesState' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { IoIosArrowDown, IoIosArrowUp } from 'react-icons/io' +import StringInput from '../../../input/String' +import { usePopoverContextClose } from '../../../util/PopoverContext' + +export type SceneElementType = { + componentJsonID: string + label: string + Icon: any + type: typeof ItemTypes.Component +} + +const ComponentListItem = ({ item }: { item: Component }) => { + const { t } = useTranslation() + useHookstate(getMutableState(ComponentEditorsState).keys).value // ensure reactively updates new components + const Icon = getState(ComponentEditorsState)[item.name]?.iconComponent ?? PlaceHolderIcon + const handleClosePopover = usePopoverContextClose() + + return ( + + ) +} + +const SceneElementListItem = ({ + categoryTitle, + categoryItems, + isCollapsed +}: { + categoryTitle: string + categoryItems: Component[] + isCollapsed: boolean +}) => { + const open = useHookstate(categoryTitle === 'Misc') + return ( + <> + +
    +
      + {categoryItems.map((item) => ( + + ))} +
    +
    + + ) +} + +const useComponentShelfCategories = (search: string) => { + useHookstate(getMutableState(ComponentShelfCategoriesState)).value + + if (!search) { + return Object.entries(getState(ComponentShelfCategoriesState)) + } + + const searchRegExp = new RegExp(search, 'gi') + + return Object.entries(getState(ComponentShelfCategoriesState)) + .map(([category, items]) => { + const filteredItems = items.filter((item) => item.name.match(searchRegExp)?.length) + return [category, filteredItems] as [string, Component[]] + }) + .filter(([_, items]) => !!items.length) +} + +export function ElementList() { + const { t } = useTranslation() + const search = useHookstate({ local: '', query: '' }) + const searchTimeout = useRef | null>(null) + + const shelves = useComponentShelfCategories(search.query.value) + + const onSearch = (text: string) => { + search.local.set(text) + if (searchTimeout.current) clearTimeout(searchTimeout.current) + searchTimeout.current = setTimeout(() => { + search.query.set(text) + }, 50) + } + + return ( + <> +
    +
    +

    {t('editor:layout.assetGrid.components')}

    + onSearch(val)} + /> +
    +
    + {shelves.map(([category, items]) => ( + + ))} + + ) + { + /* + + {t('editor:layout.assetGrid.components')} + + onSearch(e.target.value)} + inputRef={inputReference} + /> +
    + } + > + {shelves.map(([category, items]) => ( + + ))} + */ + } +} + +export default ElementList diff --git a/packages/ui/src/components/editor/panels/Properties/index.stories.tsx b/packages/ui/src/components/editor/panels/Properties/index.stories.tsx new file mode 100644 index 00000000000..37bf2160e25 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Properties', + component: Component, + parameters: { + componentSubtitle: 'PropertiesPanelTitle', + jest: 'PropertiesPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Properties/index.tsx b/packages/ui/src/components/editor/panels/Properties/index.tsx new file mode 100644 index 00000000000..f669c9af611 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/index.tsx @@ -0,0 +1,58 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import { InfoTooltip } from '../../layout/Tooltip' +//import styles from '../styles.module.scss' +import PropertiesPanelContainer from './container' + +export const PropertiesPanelTitle = () => { + const { t } = useTranslation() + + return ( +
    + + + + {t('editor:properties.title')} + + + +
    + ) +} + +export const PropertiesPanelTab: TabData = { + id: 'propertiesPanel', + closable: true, + cached: true, + title: , + content: +} + +export default PropertiesPanelTitle diff --git a/packages/ui/src/components/editor/panels/Properties/material/index.tsx b/packages/ui/src/components/editor/panels/Properties/material/index.tsx new file mode 100644 index 00000000000..dcdaa5ba9ea --- /dev/null +++ b/packages/ui/src/components/editor/panels/Properties/material/index.tsx @@ -0,0 +1,321 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import MaterialLibraryIcon from '@mui/icons-material/Yard' +import React, { useCallback, useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { Texture, Uniform } from 'three' + +import { + EntityUUID, + getComponent, + setComponent, + UndefinedEntity, + useComponent, + useOptionalComponent, + UUIDComponent +} from '@etherealengine/ecs' +import styles from '@etherealengine/editor/src/components/layout/styles.module.scss' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { getTextureAsync } from '@etherealengine/engine/src/assets/functions/resourceLoaderHooks' +import { TransparencyDitheringPlugin } from '@etherealengine/engine/src/avatar/components/TransparencyDitheringComponent' +import { SourceComponent } from '@etherealengine/engine/src/scene/components/SourceComponent' +import { setMaterialName } from '@etherealengine/engine/src/scene/materials/functions/materialSourcingFunctions' +import { NO_PROXY, none, State, useHookstate } from '@etherealengine/hyperflux' +import createReadableTexture from '@etherealengine/spatial/src/renderer/functions/createReadableTexture' +import { getDefaultType } from '@etherealengine/spatial/src/renderer/materials/constants/DefaultArgs' +import { + MaterialComponent, + MaterialComponents, + pluginByName, + prototypeByName +} from '@etherealengine/spatial/src/renderer/materials/MaterialComponent' +import { formatMaterialArgs } from '@etherealengine/spatial/src/renderer/materials/materialFunctions' +import Button from '../../../../../primitives/tailwind/Button' +import InputGroup from '../../../input/Group' +import SelectInput from '../../../input/Select' +import StringInput from '../../../input/String' +import { PanelDragContainer, PanelIcon, PanelTitle } from '../../../layout/Panel' +import { InfoTooltip } from '../../../layout/Tooltip' +import ParameterInput from '../../../properties/parameter' + +type ThumbnailData = { + src: string + blob: string +} + +const toBlobs = (thumbnails: Record): Record => { + const blobs = {} + Object.entries(thumbnails).map(([k, { blob }]) => { + blobs[k] = blob + }) + return blobs +} + +export function MaterialEditor(props: { materialUUID: EntityUUID }) { + const { t } = useTranslation() + const prototypes = Object.keys(prototypeByName).map((prototype) => ({ + label: prototype, + value: prototype + })) + + const entity = UUIDComponent.getEntityByUUID(props.materialUUID) + const materialComponent = useComponent(entity, MaterialComponent[MaterialComponents.State]) + const material = materialComponent.material.value! + const thumbnails = useHookstate>({}) + const textureUnloadMap = useHookstate void) | undefined>>({}) + const selectedPlugin = useHookstate(TransparencyDitheringPlugin.id) + + const createThumbnail = async (field: string, texture: Texture) => { + if (texture?.isTexture) { + try { + const blob: string = (await createReadableTexture(texture, { + maxDimensions: { width: 256, height: 256 }, + url: true + })) as string + const thumbData: ThumbnailData = { + src: texture.image?.src ?? 'BLOB', + blob + } + thumbnails[field].set(thumbData) + return Promise.resolve() + } catch (e) { + console.warn('failed loading thumbnail: ' + e) + } + } + } + + const createThumbnails = async () => { + const promises = Object.entries(material).map(([field, texture]: [string, Texture]) => + createThumbnail(field, texture) + ) + return Promise.all(promises) + } + + const checkThumbs = async () => { + thumbnails.promised && (await thumbnails.promise) + const thumbnailVals = thumbnails.value + Object.entries(thumbnailVals).map(([k, { blob }]) => { + if (!material[k]) { + URL.revokeObjectURL(blob) + thumbnails[k].set(none) + } + }) + await Promise.all( + Object.entries(material).map(async ([field, texture]: [string, Texture]) => { + if (texture?.isTexture) { + if (!thumbnails[field]?.value || thumbnails[field]?.value?.src !== texture.image?.src) + await createThumbnail(field, texture) + } + }) + ) + } + + const clearThumbs = useCallback(async () => { + Object.values(thumbnails.value).map(({ blob }) => URL.revokeObjectURL(blob)) + thumbnails.set({}) + }, [materialComponent, materialComponent.prototypeEntity]) + + const prototypeName = useHookstate('') + const materialName = useHookstate('') + materialName.set(material.name) + prototypeName.set(material.type) + + useEffect(() => { + clearThumbs().then(createThumbnails).then(checkThumbs) + }, [materialName, prototypeName]) + + const prototypeEntity = materialComponent.prototypeEntity.value! + const prototype = useComponent(prototypeEntity, MaterialComponent[MaterialComponents.Prototype]) + + const shouldLoadTexture = async (value, key: string, parametersObject: State) => { + let prop + if (parametersObject[key].type.value === 'texture') { + if (value) { + const priorUnload = textureUnloadMap.get(NO_PROXY)[key] + if (priorUnload) { + priorUnload() + } + const [texture, unload] = await getTextureAsync(value) + textureUnloadMap.merge({ [key]: unload }) + prop = texture + } else { + prop = null + } + } else { + prop = value + } + return prop + } + + const pluginEntity = useHookstate(UndefinedEntity) + const pluginState = useOptionalComponent(pluginEntity.value, MaterialComponent[MaterialComponents.Plugin]) + /**@todo plugin UI parameter values are autogenerated - autogenerate for prototype values rather than storing in component */ + //for each parameter type, default values + const pluginParameters = useHookstate({}) + //for the current values of the parameters + const pluginValues = useHookstate({}) + + useEffect(() => { + pluginValues.set({}) + pluginParameters.set({}) + }, [materialName]) + + useEffect(() => { + if (pluginState?.pluginEntities.value?.length) return + const uniformParameters = pluginState?.parameters?.value + const pluginParameterValues = {} + Object.entries( + uniformParameters && uniformParameters[materialName.value] ? uniformParameters[materialName.value] : {} + ).map(([key, uniform]) => { + const value = (uniform as Uniform).value + pluginParameterValues[key] = { type: getDefaultType(value), default: value } + }) + + pluginParameters.set(formatMaterialArgs(pluginParameterValues)) + + if (!pluginState?.parameters.value || !pluginState.parameters[materialName.value].value) return + for (const key in pluginState.parameters[materialName.value].value) { + pluginValues[key].set(pluginState.parameters[materialName.value].value[key].value) + } + }, [materialName, selectedPlugin, pluginState?.parameters[materialName.value]]) + + return ( +
    + + setMaterialName(entity, name)} + /> + + +
    +
    + +
    +
    {getComponent(entity, SourceComponent)}
    +
    +
    +
    + + { + if (materialComponent.prototypeEntity.value) materialComponent.prototypeEntity.set(prototypeByName[protoId]) + prototypeName.set(protoId as string) + }} + /> + + + async (value) => { + const property = await shouldLoadTexture(value, key, prototype.prototypeArguments) + EditorControlFunctions.modifyMaterial( + [materialComponent.material.value!.uuid], + materialComponent.material.value!.uuid as EntityUUID, + [{ [key]: property }] + ) + if (materialComponent.parameters.value) materialComponent.parameters[key].set(property) + }} + defaults={prototype.prototypeArguments!.value} + thumbnails={toBlobs(thumbnails.value)} + /> + +
    +
    + ({ label: key, value: key }))} + onChange={(value) => selectedPlugin.set(value as string)} + /> + +
    + {!!materialComponent.pluginEntities.value?.length && ( +
    + async (value) => { + const property = await shouldLoadTexture(value, key, pluginParameters) + getComponent(pluginEntity.value, MaterialComponent[MaterialComponents.Plugin]).parameters![ + materialName.value + ][key].value = property + pluginValues[key].set(property) + }} + defaults={pluginParameters.value} + /> + +
    + )} +
    + ) +} + +export const MaterialPropertyTitle = () => { + const { t } = useTranslation() + + return ( +
    + + + + + {t('editor:properties.mesh.materialProperties.title')} + + + +
    + ) +} + +export default MaterialEditor diff --git a/packages/ui/src/components/editor/panels/Scenes/container/index.stories.tsx b/packages/ui/src/components/editor/panels/Scenes/container/index.stories.tsx new file mode 100644 index 00000000000..679096220ea --- /dev/null +++ b/packages/ui/src/components/editor/panels/Scenes/container/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Scene/Container', + component: Component, + parameters: { + componentSubtitle: 'ScenePanelContainer', + jest: 'ScenePanelContainer.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Scenes/container/index.tsx b/packages/ui/src/components/editor/panels/Scenes/container/index.tsx new file mode 100644 index 00000000000..447a2be228a --- /dev/null +++ b/packages/ui/src/components/editor/panels/Scenes/container/index.tsx @@ -0,0 +1,160 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import config from '@etherealengine/common/src/config' +import { AssetType, assetPath } from '@etherealengine/common/src/schema.type.module' +import { useClickOutside } from '@etherealengine/common/src/utils/useClickOutside' +import { deleteScene, onNewScene } from '@etherealengine/editor/src/functions/sceneFunctions' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { getMutableState, useHookstate, useMutableState } from '@etherealengine/hyperflux' +import { useFind } from '@etherealengine/spatial/src/common/functions/FeathersHooks' +import React, { useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { HiDotsHorizontal } from 'react-icons/hi' +import { HiOutlinePlusCircle } from 'react-icons/hi2' +import Button from '../../../../../primitives/tailwind/Button' +import LoadingView from '../../../../../primitives/tailwind/LoadingView' +import Text from '../../../../../primitives/tailwind/Text' +import ConfirmDialog from '../../../../tailwind/ConfirmDialog' +import RenameSceneModal from '../modals/RenameScene' + +export default function ScenesPanel() { + const { t } = useTranslation() + const editorState = useMutableState(EditorState) + const scenesQuery = useFind(assetPath, { query: { project: editorState.projectName.value } }) + const scenes = scenesQuery.data + + const contextMenuRef = useRef(null) + const isContextMenuOpen = useHookstate('') + const scenesLoading = scenesQuery.status === 'pending' + const onCreateScene = async () => onNewScene() + + const onClickScene = (scene: AssetType) => { + getMutableState(EditorState).scenePath.set(scene.assetURL) + } + + const deleteSelectedScene = async (scene: AssetType) => { + if (scene) { + await deleteScene(scene.id) + if (editorState.sceneAssetID.value === scene.id) { + editorState.sceneName.set(null) + editorState.sceneAssetID.set(null) + } + } + PopoverState.hidePopupover() + } + + const getSceneName = (scene: AssetType) => + scene.assetURL.split('/').pop()!.replace('.gltf', '').replace('.scene.json', '') + + useClickOutside(contextMenuRef, () => isContextMenuOpen.set('')) + + return ( +
    +
    + +
    +
    + {scenesLoading ? ( + + ) : ( +
    +
    + {scenes.map((scene: AssetType) => ( +
    + {scene.assetURL} { + e.currentTarget.src = 'static/etherealengine_logo.png' + }} + crossOrigin="anonymous" + className="block h-[100%] w-auto cursor-pointer rounded-t-lg object-cover" + onClick={() => onClickScene(scene)} + /> +
    + {getSceneName(scene)} +
    + + +
    + ) : null} +
    +
    +
    + ))} +
    +
    + )} +
    +
    + ) +} diff --git a/packages/ui/src/components/editor/panels/Scenes/index.stories.tsx b/packages/ui/src/components/editor/panels/Scenes/index.stories.tsx new file mode 100644 index 00000000000..730472a9336 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Scenes/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Scene', + component: Component, + parameters: { + componentSubtitle: 'ScenePanelTitle', + jest: 'ScenePanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Scenes/index.tsx b/packages/ui/src/components/editor/panels/Scenes/index.tsx new file mode 100644 index 00000000000..d7150a74d48 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Scenes/index.tsx @@ -0,0 +1,55 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import ScenesPanel from './container' + +/** + * Displays the scenes that exist in the current project. + */ +export const ScenePanelTitle = () => { + const { t } = useTranslation() + + return ( +
    + + {t('editor:properties.scene.name')} + +
    + ) +} + +export const ScenePanelTab: TabData = { + id: 'scenePanel', + closable: true, + cached: true, + title: , + content: +} + +export default ScenePanelTitle diff --git a/packages/ui/src/components/editor/panels/Scenes/modals/RenameScene.tsx b/packages/ui/src/components/editor/panels/Scenes/modals/RenameScene.tsx new file mode 100644 index 00000000000..0b91c98c2fc --- /dev/null +++ b/packages/ui/src/components/editor/panels/Scenes/modals/RenameScene.tsx @@ -0,0 +1,60 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' + +import { AssetType } from '@etherealengine/common/src/schema.type.module' +import { renameScene } from '@etherealengine/editor/src/functions/sceneFunctions' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import Input from '../../../../../primitives/tailwind/Input' +import Modal from '../../../../../primitives/tailwind/Modal' + +export default function RenameSceneModal({ sceneName, scene }: { sceneName: string; scene: AssetType }) { + const { t } = useTranslation() + const newSceneName = useHookstate(sceneName) + + const handleSubmit = async () => { + const currentURL = scene.assetURL + const newURL = currentURL.replace(currentURL.split('/').pop()!, newSceneName.value + '.gltf') + const newData = await renameScene(scene.id, newURL, scene.projectName) + getMutableState(EditorState).scenePath.set(newData.assetURL) + PopoverState.hidePopupover() + } + + return ( + + newSceneName.set(event.target.value)} /> + + ) +} diff --git a/packages/ui/src/components/editor/panels/Viewport/container/index.stories.tsx b/packages/ui/src/components/editor/panels/Viewport/container/index.stories.tsx new file mode 100644 index 00000000000..11cee1984a4 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/container/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Viewport/Container', + component: Component, + parameters: { + componentSubtitle: 'ViewportPanelContainer', + jest: 'ViewportPanelContainer.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Viewport/container/index.tsx b/packages/ui/src/components/editor/panels/Viewport/container/index.tsx new file mode 100644 index 00000000000..89bb80d05fd --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/container/index.tsx @@ -0,0 +1,126 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { Engine, getComponent } from '@etherealengine/ecs' +import { SceneElementType } from '@etherealengine/editor/src/components/element/ElementList' +import { ItemTypes } from '@etherealengine/editor/src/constants/AssetTypes' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { getCursorSpawnPosition } from '@etherealengine/editor/src/functions/screenSpaceFunctions' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { TransformComponent } from '@etherealengine/spatial' +import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' +import React, { useEffect, useRef } from 'react' +import { useDrop } from 'react-dnd' +import { useTranslation } from 'react-i18next' +import { twMerge } from 'tailwind-merge' +import { Vector2, Vector3 } from 'three' +import Text from '../../../../../primitives/tailwind/Text' +import GridTool from '../tools/GridTool' +import PlayModeTool from '../tools/PlayModeTool' +import RenderModeTool from '../tools/RenderTool' +import TransformPivotTool from '../tools/TransformPivotTool' +import TransformSnapTool from '../tools/TransformSnapTool' +import TransformSpaceTool from '../tools/TransformSpaceTool' + +const ViewportDnD = () => { + const ref = useRef(null as null | HTMLDivElement) + + const [{ isDragging, isOver }, dropRef] = useDrop({ + accept: [ItemTypes.Component], + collect: (monitor) => ({ + isDragging: monitor.getItem() !== null && monitor.canDrop(), + isOver: monitor.isOver() + }), + drop(item: SceneElementType, monitor) { + const vec3 = new Vector3() + getCursorSpawnPosition(monitor.getClientOffset() as Vector2, vec3) + EditorControlFunctions.createObjectFromSceneElement([ + { name: item!.componentJsonID }, + { name: TransformComponent.jsonID, props: { position: vec3 } } + ]) + } + }) + + useEffect(() => { + if (!ref?.current) return + + const canvas = getComponent(Engine.instance.viewerEntity, RendererComponent).renderer.domElement + ref.current.appendChild(canvas) + + getComponent(Engine.instance.viewerEntity, RendererComponent).needsResize = true + + const observer = new ResizeObserver(() => { + getComponent(Engine.instance.viewerEntity, RendererComponent).needsResize = true + }) + + observer.observe(ref.current) + return () => { + observer.disconnect() + // const canvas = document.getElementById('engine-renderer-canvas')! + // parent.removeChild(canvas) + } + }, [ref]) + + return ( +
    dropRef(el)) && ref} + className={twMerge( + 'h-full w-full border border-white', + isDragging && isOver ? 'border-4' : 'border-none', + isDragging ? 'pointer-events-auto' : 'pointer-events-none' + )} + >
    + ) +} + +const ViewPortPanelContainer = () => { + const { t } = useTranslation() + const sceneName = useHookstate(getMutableState(EditorState).sceneName).value + return ( +
    +
    + + + + +
    + + +
    + {sceneName ? ( + + ) : ( +
    + + {t('editor:selectSceneMsg')} +
    + )} +
    + ) +} + +export default ViewPortPanelContainer diff --git a/packages/ui/src/components/editor/panels/Viewport/index.stories.tsx b/packages/ui/src/components/editor/panels/Viewport/index.stories.tsx new file mode 100644 index 00000000000..6a504077add --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/Viewport', + component: Component, + parameters: { + componentSubtitle: 'ViewportPanelTitle', + jest: 'ViewportPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/Viewport/index.tsx b/packages/ui/src/components/editor/panels/Viewport/index.tsx new file mode 100644 index 00000000000..0f48e7829ce --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/index.tsx @@ -0,0 +1,50 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' +import ViewPortPanelContainer from './container' + +export const ViewportPanelTitle = () => { + const { t } = useTranslation() + + return ( + + {t('editor:viewport.title')} + + ) +} + +export default ViewportPanelTitle + +export const ViewportPanelTab: TabData = { + id: 'viewPanel', + closable: true, + title: , + content: +} diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/GridTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/GridTool.tsx new file mode 100644 index 00000000000..e7195bd5ce4 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/tools/GridTool.tsx @@ -0,0 +1,69 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { RendererState } from '@etherealengine/spatial/src/renderer/RendererState' +import { MdBorderClear } from 'react-icons/md' + +import Button from '../../../../../primitives/tailwind/Button' +import NumericInput from '../../../input/Numeric' + +const GridTool = () => { + const { t } = useTranslation() + + const rendererState = useHookstate(getMutableState(RendererState)) + + const onToggleGridVisible = () => { + rendererState.gridVisibility.set(!rendererState.gridVisibility.value) + } + + return ( +
    +
    + ) +} + +export default GridTool diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/PlayModeTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/PlayModeTool.tsx new file mode 100644 index 00000000000..75c7893b30b --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/tools/PlayModeTool.tsx @@ -0,0 +1,115 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { AuthState } from '@etherealengine/client-core/src/user/services/AuthService' +import { UUIDComponent } from '@etherealengine/ecs' +import { getComponent, getOptionalComponent, removeComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { Engine } from '@etherealengine/ecs/src/Engine' +import { removeEntity } from '@etherealengine/ecs/src/EntityFunctions' +import { TransformGizmoControlledComponent } from '@etherealengine/editor/src/classes/TransformGizmoControlledComponent' +import { EditorState } from '@etherealengine/editor/src/services/EditorServices' +import { transformGizmoControlledQuery } from '@etherealengine/editor/src/systems/GizmoSystem' +import { VisualScriptActions, visualScriptQuery } from '@etherealengine/engine' +import { AvatarComponent } from '@etherealengine/engine/src/avatar/components/AvatarComponent' +import { getRandomSpawnPoint } from '@etherealengine/engine/src/avatar/functions/getSpawnPoint' +import { spawnLocalAvatarInWorld } from '@etherealengine/engine/src/avatar/functions/receiveJoinWorld' +import { GLTFComponent } from '@etherealengine/engine/src/gltf/GLTFComponent' +import { dispatchAction, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' +import { WorldNetworkAction } from '@etherealengine/network' +import { EngineState } from '@etherealengine/spatial/src/EngineState' +import { FollowCameraComponent } from '@etherealengine/spatial/src/camera/components/FollowCameraComponent' +import { TargetCameraRotationComponent } from '@etherealengine/spatial/src/camera/components/TargetCameraRotationComponent' +import { ComputedTransformComponent } from '@etherealengine/spatial/src/transform/components/ComputedTransformComponent' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { HiOutlinePause, HiOutlinePlay } from 'react-icons/hi2' +import Button from '../../../../../primitives/tailwind/Button' +import Tooltip from '../../../../../primitives/tailwind/Tooltip' + +const PlayModeTool = () => { + const { t } = useTranslation() + + const isEditing = useHookstate(getMutableState(EngineState).isEditing) + const authState = useHookstate(getMutableState(AuthState)) + + const sceneEntity = useHookstate(getMutableState(EditorState).rootEntity) + const gltfComponent = getOptionalComponent(sceneEntity.value, GLTFComponent) + + const onTogglePlayMode = () => { + const entity = AvatarComponent.getSelfAvatarEntity() + if (entity) { + dispatchAction(WorldNetworkAction.destroyEntity({ entityUUID: getComponent(entity, UUIDComponent) })) + removeEntity(entity) + removeComponent(Engine.instance.cameraEntity, ComputedTransformComponent) + removeComponent(Engine.instance.cameraEntity, FollowCameraComponent) + removeComponent(Engine.instance.cameraEntity, TargetCameraRotationComponent) + getMutableState(EngineState).isEditing.set(true) + visualScriptQuery().forEach((entity) => dispatchAction(VisualScriptActions.stop({ entity }))) + // stop all visual script logic + } else { + const avatarDetails = authState.user.avatar.value + + const avatarSpawnPose = getRandomSpawnPoint(Engine.instance.userID) + const currentScene = getComponent(getState(EditorState).rootEntity, UUIDComponent) + + if (avatarDetails) + spawnLocalAvatarInWorld({ + parentUUID: currentScene, + avatarSpawnPose, + avatarID: avatarDetails.id!, + name: authState.user.name.value + }) + + // todo + getMutableState(EngineState).isEditing.set(false) + // run all visual script logic + visualScriptQuery().forEach((entity) => dispatchAction(VisualScriptActions.execute({ entity }))) + transformGizmoControlledQuery().forEach((entity) => removeComponent(entity, TransformGizmoControlledComponent)) + //just remove all gizmo in the scene + } + } + + return ( +
    + +
    + ) +} + +export default PlayModeTool diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/RenderTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/RenderTool.tsx new file mode 100644 index 00000000000..e005c57c227 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/tools/RenderTool.tsx @@ -0,0 +1,147 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { ShadowMapResolutionOptions } from '@etherealengine/client-core/src/user/components/UserMenu/menus/SettingMenu' +import { useHookstate, useMutableState } from '@etherealengine/hyperflux' +import { RendererState } from '@etherealengine/spatial/src/renderer/RendererState' +import { RenderModes, RenderModesType } from '@etherealengine/spatial/src/renderer/constants/RenderModes' +import { GiWireframeGlobe } from 'react-icons/gi' +import { RiArrowDownSLine } from 'react-icons/ri' +import { TbBallBowling, TbInnerShadowBottom, TbInnerShadowBottomFilled, TbShadow } from 'react-icons/tb' +import { ViewportPanelTab } from '..' +import Button from '../../../../../primitives/tailwind/Button' +import Tooltip from '../../../../../primitives/tailwind/Tooltip' +import BooleanInput from '../../../input/Boolean' +import InputGroup from '../../../input/Group' +import SelectInput from '../../../input/Select' +import PopOver from '../../../layout/Popover' + +const renderModes: { name: RenderModesType; icon: JSX.Element }[] = [ + { + name: 'Unlit', + icon: + }, + { + name: 'Lit', + icon: + }, + { name: 'Normals', icon: }, + { + name: 'Wireframe', + icon: + }, + { + name: 'Shadows', + icon: + } +] + +const RenderModeTool = () => { + const { t } = useTranslation() + const anchorEl = useHookstate(null) + const anchorPosition = useHookstate({ left: 0, top: 0 }) + + const rendererState = useMutableState(RendererState) + const options = [] as { label: string; value: string }[] + const isVisible = useHookstate(false) + + for (let key of Object.keys(RenderModes)) { + options.push({ + label: RenderModes[key], + value: RenderModes[key] + }) + } + + const onChangeRenderMode = (mode: RenderModesType) => { + rendererState.renderMode.set(mode) + } + + const handlePostProcessingChange = () => { + rendererState.usePostProcessing.set(!rendererState.usePostProcessing.value) + rendererState.automatic.set(false) + } + + return ( +
    + {renderModes.map((mode) => ( + +
    + ) +} + +export default RenderModeTool diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx new file mode 100644 index 00000000000..1358652dc80 --- /dev/null +++ b/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx @@ -0,0 +1,88 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { TransformPivot } from '@etherealengine/engine/src/scene/constants/transformConstants' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' + +import { setTransformPivot, toggleTransformPivot } from '@etherealengine/editor/src/functions/transformFunctions' +import { EditorHelperState } from '@etherealengine/editor/src/services/EditorHelperState' +import { t } from 'i18next' +import { useTranslation } from 'react-i18next' +import { FaRegDotCircle } from 'react-icons/fa' +import Button from '../../../../../primitives/tailwind/Button' +import Select from '../../../../../primitives/tailwind/Select' + +const transformPivotOptions = [ + { + label: t('editor:toolbar.transformPivot.lbl-selection'), + description: t('editor:toolbar.transformPivot.info-selection'), + value: TransformPivot.Selection + }, + { + label: t('editor:toolbar.transformPivot.lbl-center'), + description: t('editor:toolbar.transformPivot.info-center'), + value: TransformPivot.Center + }, + { + label: t('editor:toolbar.transformPivot.lbl-bottom'), + description: t('editor:toolbar.transformPivot.info-bottom'), + value: TransformPivot.Bottom + }, + { + label: t('editor:toolbar.transformPivot.lbl-origin'), + description: t('editor:toolbar.transformPivot.info-origin'), + value: TransformPivot.Origin + } +] + +const TransformPivotTool = () => { + const { t } = useTranslation() + + const editorHelperState = useHookstate(getMutableState(EditorHelperState)) + + return ( +
    +
    + ) +} + +export default TransformSpaceTool diff --git a/packages/ui/src/components/editor/panels/VisualScript/index.stories.tsx b/packages/ui/src/components/editor/panels/VisualScript/index.stories.tsx new file mode 100644 index 00000000000..55507d790a2 --- /dev/null +++ b/packages/ui/src/components/editor/panels/VisualScript/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Panel/VisualScript', + component: Component, + parameters: { + componentSubtitle: 'VisualScriptPanelTitle', + jest: 'VisualScriptPanelTitle.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/panels/VisualScript/index.tsx b/packages/ui/src/components/editor/panels/VisualScript/index.tsx new file mode 100644 index 00000000000..13b59064faf --- /dev/null +++ b/packages/ui/src/components/editor/panels/VisualScript/index.tsx @@ -0,0 +1,53 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { TabData } from 'rc-dock' +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PanelDragContainer, PanelTitle } from '../../layout/Panel' + +export const VisualScriptPanelTitle = () => { + const { t } = useTranslation() + + return ( +
    + + + {'VisualScript'} + + +
    + ) +} + +export default VisualScriptPanelTitle + +export const VisualScriptPanelTab: TabData = { + id: 'visualScriptPanel', + closable: true, + title: , + content: <> +} diff --git a/packages/ui/src/components/editor/panels/preview/material/index.tsx b/packages/ui/src/components/editor/panels/preview/material/index.tsx new file mode 100644 index 00000000000..ff208a28d44 --- /dev/null +++ b/packages/ui/src/components/editor/panels/preview/material/index.tsx @@ -0,0 +1,93 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect, useRef } from 'react' +import { Mesh, SphereGeometry } from 'three' + +import { useRender3DPanelSystem } from '@etherealengine/client-core/src/user/components/Panel3D/useRender3DPanelSystem' +import { generateEntityUUID, getComponent, getMutableComponent, setComponent, UUIDComponent } from '@etherealengine/ecs' +import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' +import { MaterialSelectionState } from '@etherealengine/engine/src/scene/materials/MaterialLibraryState' +import { getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' +import { CameraOrbitComponent } from '@etherealengine/spatial/src/camera/components/CameraOrbitComponent' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { addObjectToGroup } from '@etherealengine/spatial/src/renderer/components/GroupComponent' +import { VisibleComponent } from '@etherealengine/spatial/src/renderer/components/VisibleComponent' +import { getMaterial } from '@etherealengine/spatial/src/renderer/materials/materialFunctions' +import { RendererComponent } from '@etherealengine/spatial/src/renderer/WebGLRendererSystem' +import { MaterialsPanelTab } from '../../Materials' + +export const MaterialPreviewCanvas = () => { + const panelRef = useRef() as React.MutableRefObject + const renderPanel = useRender3DPanelSystem(panelRef) + const selectedMaterial = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial) + const panel = document.getElementById(MaterialsPanelTab.id!) + + useEffect(() => { + if (!selectedMaterial.value) return + const { sceneEntity, cameraEntity } = renderPanel + setComponent(sceneEntity, NameComponent, 'Material Preview Entity') + const uuid = generateEntityUUID() + setComponent(sceneEntity, UUIDComponent, uuid) + setComponent(sceneEntity, VisibleComponent, true) + const material = getMaterial(getState(MaterialSelectionState).selectedMaterial!) + if (!material) return + addObjectToGroup(sceneEntity, new Mesh(new SphereGeometry(5, 32, 32), material)) + setComponent(sceneEntity, EnvmapComponent, { type: 'Skybox', envMapIntensity: 2 }) + const orbitCamera = getMutableComponent(cameraEntity, CameraOrbitComponent) + orbitCamera.focusedEntities.set([sceneEntity]) + orbitCamera.refocus.set(true) + }, [selectedMaterial]) + + useEffect(() => { + if (!panelRef?.current) return + if (!panel) return + getComponent(renderPanel.cameraEntity, RendererComponent).needsResize = true + + const observer = new ResizeObserver(() => { + getComponent(renderPanel.cameraEntity, RendererComponent).needsResize = true + }) + + observer.observe(panel) + + return () => { + observer.disconnect() + } + }, [panelRef]) + + return ( + <> +
    + +
    + + ) +} + +export const MaterialPreviewPanel = (props) => { + const selectedMaterial = useHookstate(getMutableState(MaterialSelectionState).selectedMaterial) + if (!selectedMaterial.value) return null + return +} diff --git a/packages/ui/src/components/editor/properties/animation/index.stories.tsx b/packages/ui/src/components/editor/properties/animation/index.stories.tsx new file mode 100644 index 00000000000..acb3cd8fb04 --- /dev/null +++ b/packages/ui/src/components/editor/properties/animation/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Animation', + component: Component, + parameters: { + componentSubtitle: 'AnimationNodeEditor', + jest: 'animationNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/animation/index.tsx b/packages/ui/src/components/editor/properties/animation/index.tsx new file mode 100644 index 00000000000..23af87bcfea --- /dev/null +++ b/packages/ui/src/components/editor/properties/animation/index.tsx @@ -0,0 +1,118 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { getOptionalComponent, useComponent, useOptionalComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperties, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { AnimationComponent } from '@etherealengine/engine/src/avatar/components/AnimationComponent' +import { LoopAnimationComponent } from '@etherealengine/engine/src/avatar/components/LoopAnimationComponent' +import { getEntityErrors } from '@etherealengine/engine/src/scene/components/ErrorComponent' +import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' +import { useState } from '@etherealengine/hyperflux' +import { getCallback } from '@etherealengine/spatial/src/common/CallbackComponent' +import StreetviewIcon from '@mui/icons-material/Streetview' +import { VRM } from '@pixiv/three-vrm' +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { SelectOptionsType } from '../../../../primitives/tailwind/Select' +import InputGroup from '../../input/Group' +import ModelInput from '../../input/Model' +import NumericInput from '../../input/Numeric' +import ProgressBar from '../../input/Progress' +import SelectInput from '../../input/Select' +import NodeEditor from '../nodeEditor' + +export const LoopAnimationNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const entity = props.entity + + const animationOptions = useState([] as { label: string; value: number }[]) + const loopAnimationComponent = useComponent(entity, LoopAnimationComponent) + + const modelComponent = useOptionalComponent(entity, ModelComponent) + const animationComponent = useOptionalComponent(entity, AnimationComponent) + + const errors = getEntityErrors(props.entity, ModelComponent) + + useEffect(() => { + const animationComponent = getOptionalComponent(entity, AnimationComponent) + if (!animationComponent || !animationComponent.animations.length) return + animationOptions.set([ + { label: 'None', value: -1 }, + ...animationComponent.animations.map((clip, index) => ({ label: clip.name, value: index })) + ]) + }, [modelComponent?.asset, modelComponent?.convertToVRM, animationComponent?.animations]) + + const onChangePlayingAnimation = (index) => { + commitProperties(LoopAnimationComponent, { + activeClipIndex: index + }) + getCallback(props.entity, 'xre.play')!() + } + + return ( + + + + + + {modelComponent?.asset.value instanceof VRM && ( + + + {errors?.LOADING_ERROR && ( +
    {t('editor:properties.model.error-url')}
    + )} +
    + )} + + + +
    + ) +} + +LoopAnimationNodeEditor.iconComponent = StreetviewIcon + +export default LoopAnimationNodeEditor diff --git a/packages/ui/src/components/editor/properties/camera/index.stories.tsx b/packages/ui/src/components/editor/properties/camera/index.stories.tsx new file mode 100644 index 00000000000..c6a31fe992c --- /dev/null +++ b/packages/ui/src/components/editor/properties/camera/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Camera', + component: Component, + parameters: { + componentSubtitle: 'CameraNodeEditor', + jest: 'cameraNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/camera/index.tsx b/packages/ui/src/components/editor/properties/camera/index.tsx new file mode 100644 index 00000000000..33ba3d544c9 --- /dev/null +++ b/packages/ui/src/components/editor/properties/camera/index.tsx @@ -0,0 +1,240 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { t } from 'i18next' +import React from 'react' + +import { getOptionalComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { CameraSettingsComponent } from '@etherealengine/engine/src/scene/components/CameraSettingsComponent' +import { CameraMode } from '@etherealengine/spatial/src/camera/types/CameraMode' + +import { defineQuery } from '@etherealengine/ecs/src/QueryFunctions' +import { + EditorComponentType, + commitProperties, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { ModelComponent } from '@etherealengine/engine/src/scene/components/ModelComponent' +import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent' +import { iterateEntityNode } from '@etherealengine/spatial/src/transform/components/EntityTree' +import { Button } from '@mui/material' +import { HiOutlineCamera } from 'react-icons/hi' +import { Box3, Vector3 } from 'three' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import PropertyGroup from '../group' + +/** Types copied from Camera Modes of engine. */ +const cameraModeSelect = [ + { + label: 'First Person', + value: CameraMode.FirstPerson + }, + { + label: 'Shoulder Cam', + value: CameraMode.ShoulderCam + }, + { + label: 'Third Person', + value: CameraMode.ThirdPerson + }, + { + label: 'Top Down', + value: CameraMode.TopDown + }, + { + label: 'Strategic', + value: CameraMode.Strategic + }, + { + label: 'Dynamic', + value: CameraMode.Dynamic + } +] + +/** Types copied from Camera Modes of engine. */ +const projectionTypeSelect = [ + { + label: 'Orthographic', + value: 0 + }, + { + label: 'Perspective', + value: 1 + } +] + +const modelQuery = defineQuery([ModelComponent]) +const _box3 = new Box3() + +export const CameraPropertiesNodeEditor: EditorComponentType = (props) => { + const cameraSettings = useComponent(props.entity, CameraSettingsComponent) + + const calculateClippingPlanes = () => { + const box = new Box3() + const modelEntities = modelQuery() + for (const entity of modelEntities) { + console.log(entity) + iterateEntityNode(entity, (entity) => { + const mesh = getOptionalComponent(entity, MeshComponent) + if (mesh?.geometry?.boundingBox) { + console.log(mesh) + _box3.copy(mesh.geometry.boundingBox) + _box3.applyMatrix4(mesh.matrixWorld) + box.union(_box3) + } + }) + } + const boxSize = box.getSize(new Vector3()).length() + commitProperties( + CameraSettingsComponent, + { + cameraNearClip: 0.1, + cameraFarClip: Math.max(boxSize, 100) + }, + [props.entity] + ) + } + + return ( + } + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +export default CameraPropertiesNodeEditor diff --git a/packages/ui/src/components/editor/properties/collider/index.stories.tsx b/packages/ui/src/components/editor/properties/collider/index.stories.tsx new file mode 100644 index 00000000000..4e522c21fe7 --- /dev/null +++ b/packages/ui/src/components/editor/properties/collider/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Collider', + component: Component, + parameters: { + componentSubtitle: 'ColliderNodeEditor', + jest: 'ColliderNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/collider/index.tsx b/packages/ui/src/components/editor/properties/collider/index.tsx new file mode 100644 index 00000000000..6eea49e37b7 --- /dev/null +++ b/packages/ui/src/components/editor/properties/collider/index.tsx @@ -0,0 +1,106 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { camelCaseToSpacedString } from '@etherealengine/common/src/utils/camelCaseToSpacedString' +import { useComponent } from '@etherealengine/ecs' +import { EditorComponentType, commitProperty } from '@etherealengine/editor/src/components/properties/Util' +import { + ColliderComponent, + supportedColliderShapes +} from '@etherealengine/spatial/src/physics/components/ColliderComponent' +import { Shapes } from '@etherealengine/spatial/src/physics/types/PhysicsTypes' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { FiMinimize2 } from 'react-icons/fi' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import Vector3Input from '../../input/Vector3' +import NodeEditor from '../nodeEditor' + +const shapeTypeOptions = Object.entries(Shapes) + .filter(([_, value]) => supportedColliderShapes.includes(value as any)) + .map(([label, value]) => ({ + label: camelCaseToSpacedString(label), + value + })) + +export const ColliderComponentEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const colliderComponent = useComponent(props.entity, ColliderComponent) + + return ( + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +ColliderComponentEditor.iconComponent = FiMinimize2 + +export default ColliderComponentEditor diff --git a/packages/ui/src/components/editor/properties/envMapBake/index.stories.tsx b/packages/ui/src/components/editor/properties/envMapBake/index.stories.tsx new file mode 100644 index 00000000000..ddad51a6c5d --- /dev/null +++ b/packages/ui/src/components/editor/properties/envMapBake/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/EnvMapBake', + component: Component, + parameters: { + componentSubtitle: 'EnvMapBakeNodeEditor', + jest: 'envMapBakeNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/envMapBake/index.tsx b/packages/ui/src/components/editor/properties/envMapBake/index.tsx new file mode 100644 index 00000000000..861d0a52a13 --- /dev/null +++ b/packages/ui/src/components/editor/properties/envMapBake/index.tsx @@ -0,0 +1,210 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { EnvMapBakeComponent } from '@etherealengine/engine/src/scene/components/EnvMapBakeComponent' +import { EnvMapBakeTypes } from '@etherealengine/engine/src/scene/types/EnvMapBakeTypes' + +import { commitProperty, updateProperty } from '@etherealengine/editor/src/components/properties/Util' +import { uploadBPCEMBakeToServer } from '@etherealengine/editor/src/functions/uploadEnvMapBake' +import BooleanInput from '@etherealengine/ui/src/components/editor/input/Boolean' +import { IoMapOutline } from 'react-icons/io5' +import Button from '../../../../primitives/tailwind/Button' +import InputGroup from '../../input/Group' +import SelectInput from '../../input/Select' +import Vector3Input from '../../input/Vector3' +import NodeEditor from '../nodeEditor' +import EnvMapBakeProperties from './properties' + +export const enum BakePropertyTypes { + 'Boolean', + 'BakeType', + 'RefreshMode', + 'Resolution', + 'Vector' +} + +const DefaultEnvMapBakeSettings = [ + { + label: 'Bake Settings', + options: [ + { + label: 'Type', + propertyName: 'bakeType', + type: BakePropertyTypes.BakeType + }, + { + label: 'Scale', + propertyName: 'bakeScale', + type: BakePropertyTypes.Vector + } + ] + }, + { + label: 'Realtime Settings', + options: [ + { + label: 'Refresh Mode', + propertyName: 'refreshMode', + type: BakePropertyTypes.RefreshMode + } + ] + }, + + { + label: 'Settings', + options: [ + { + label: 'Box Projection', + propertyName: 'boxProjection', + type: BakePropertyTypes.Boolean + } + ] + }, + { + label: 'Capture Settings', + options: [ + { + label: 'Resolution', + propertyName: 'resolution', + type: BakePropertyTypes.Resolution + } + ] + } +] + +const bakeResolutionTypes = [256, 512, 1024, 2048] + +const titleLabelStyle = { + display: 'flex', + flexDirection: 'row', + alignItems: 'left', + fontWeight: 'bold', + color: 'var(--textColor)', + padding: '0 8px 8px', + ':last-child': { + marginLeft: 'auto' + } +} + +const envMapBakeNodeEditorStyle = {} + +export const EnvMapBakeNodeEditor = (props) => { + const bakeComponent = useComponent(props.entity, EnvMapBakeComponent) + const renderEnvMapBakeProperties = () => { + const renderedProperty = DefaultEnvMapBakeSettings.map((element, id) => { + if (element.label == 'Realtime Settings' && bakeComponent.bakeType.value == EnvMapBakeTypes.Realtime) { + return
    + } + + const renderProp = element.label + ? [ +
    + {element.label} +
    + ] + : [] + + element.options?.forEach((property, propertyid) => { + renderProp.push( + + ) + }) + + renderProp.push(
    ) + return renderProp + }) + + return renderedProperty + } + + const onChangePosition = (value) => { + bakeComponent.bakePositionOffset.value.copy(value) + } + const onChangeScale = (value) => { + bakeComponent.bakeScale.value.copy(value) + } + + return ( + } + > + + + + + + + + + + + + ({ label: resolution.toString(), value: resolution }))} + key={props.entity} + value={bakeComponent.resolution.value} + onChange={commitProperty(EnvMapBakeComponent, 'resolution')} + /> + + + + + + ) +} + +EnvMapBakeNodeEditor.iconComponent = IoMapOutline +export default EnvMapBakeNodeEditor diff --git a/packages/ui/src/components/editor/properties/envMapBake/properties/index.stories.tsx b/packages/ui/src/components/editor/properties/envMapBake/properties/index.stories.tsx new file mode 100644 index 00000000000..81629dc4e8b --- /dev/null +++ b/packages/ui/src/components/editor/properties/envMapBake/properties/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/EnvMapBake/Properties', + component: Component, + parameters: { + componentSubtitle: 'EnvMapBakePropertiesNodeEditor', + jest: 'envMapBakePropertiesNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/envMapBake/properties/index.tsx b/packages/ui/src/components/editor/properties/envMapBake/properties/index.tsx new file mode 100644 index 00000000000..4165d9b7ab5 --- /dev/null +++ b/packages/ui/src/components/editor/properties/envMapBake/properties/index.tsx @@ -0,0 +1,163 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import { ComponentType } from '@etherealengine/ecs/src/ComponentFunctions' +import { Entity } from '@etherealengine/ecs/src/Entity' +import { EnvMapBakeComponent } from '@etherealengine/engine/src/scene/components/EnvMapBakeComponent' +import { EnvMapBakeRefreshTypes } from '@etherealengine/engine/src/scene/types/EnvMapBakeRefreshTypes' +import { EnvMapBakeTypes } from '@etherealengine/engine/src/scene/types/EnvMapBakeTypes' + +import { commitProperty, updateProperty } from '@etherealengine/editor/src/components/properties/Util' +import BooleanInput from '@etherealengine/ui/src/components/editor/input/Boolean' +import { BakePropertyTypes } from '..' +import InputGroup from '../../../input/Group' +import SelectInput from '../../../input/Select' +import Vector3Input from '../../../input/Vector3' + +type EnvMapBakePropertyEditorProps = { + bakeComponent: ComponentType + element: any + entity: Entity +} + +const envMapBakeSelectTypes = [ + { + label: 'Runtime', + value: EnvMapBakeTypes.Realtime + }, + { + label: 'Baked', + value: EnvMapBakeTypes.Baked + } +] + +const envMapBakeRefreshSelectTypes = [ + { + label: 'On Awake', + value: EnvMapBakeRefreshTypes.OnAwake + } + // { + // label:"Every Frame", + // value: EnvMapBakeRefreshTypes.EveryFrame, + // } +] + +const bakeResolutionTypes = [ + { + label: '128', + value: 128 + }, + { + label: '256', + value: 256 + }, + { + label: '512', + value: 512 + }, + { + label: '1024', + value: 1024 + }, + { + label: '2048', + value: 2048 + } +] + +export const EnvMapBakeProperties = (props: EnvMapBakePropertyEditorProps) => { + const getPropertyValue = (option) => props.bakeComponent[option] + + let renderVal = <> + const label = props.element.label + const propertyName = props.element.propertyName + + switch (props.element.type) { + case BakePropertyTypes.Boolean: + renderVal = ( + + ) + break + case BakePropertyTypes.BakeType: + renderVal = ( + + ) + break + + case BakePropertyTypes.RefreshMode: + renderVal = ( + + ) + break + + case BakePropertyTypes.Resolution: + renderVal = ( + + ) + break + + case BakePropertyTypes.Vector: + renderVal = ( + + ) + break + + default: + renderVal =
    Undefined value Type
    + break + } + + return ( + + {renderVal} + + ) +} + +export default EnvMapBakeProperties diff --git a/packages/ui/src/components/editor/properties/envmap/index.stories.tsx b/packages/ui/src/components/editor/properties/envmap/index.stories.tsx new file mode 100644 index 00000000000..269c9d52e76 --- /dev/null +++ b/packages/ui/src/components/editor/properties/envmap/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Envmap', + component: Component, + parameters: { + componentSubtitle: 'EnvmapNodeEditor', + jest: 'envmapNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/envmap/index.tsx b/packages/ui/src/components/editor/properties/envmap/index.tsx new file mode 100644 index 00000000000..f83d628f41a --- /dev/null +++ b/packages/ui/src/components/editor/properties/envmap/index.tsx @@ -0,0 +1,163 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' + +import { UUIDComponent } from '@etherealengine/ecs' +import { getComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { EnvMapBakeComponent } from '@etherealengine/engine/src/scene/components/EnvMapBakeComponent' +import { EnvmapComponent } from '@etherealengine/engine/src/scene/components/EnvmapComponent' +import { getEntityErrors } from '@etherealengine/engine/src/scene/components/ErrorComponent' +import { EnvMapSourceType, EnvMapTextureType } from '@etherealengine/engine/src/scene/constants/EnvMapEnum' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' + +import { useQuery } from '@etherealengine/ecs/src/QueryFunctions' +import { + EditorComponentType, + commitProperty, + updateProperties, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { IoMapOutline } from 'react-icons/io5' +import ColorInput from '../../../../primitives/tailwind/Color' +import Slider from '../../../../primitives/tailwind/Slider' +import FolderInput from '../../input/Folder' +import InputGroup from '../../input/Group' +import ImagePreviewInput from '../../input/Image/Preview' +import SelectInput from '../../input/Select' +import NodeEditor from '../nodeEditor' + +/** + * EnvMapSourceOptions array containing SourceOptions for Envmap + */ +const EnvMapSourceOptions = Object.values(EnvMapSourceType).map((value) => ({ label: value, value })) + +/** + * EnvMapSourceOptions array containing SourceOptions for Envmap + */ +const EnvMapTextureOptions = Object.values(EnvMapTextureType).map((value) => ({ label: value, value })) + +/** + * EnvMapEditor provides the editor view for environment map property customization. + */ +export const EnvMapEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const entity = props.entity + + const bakeEntities = useQuery([EnvMapBakeComponent]).map((entity) => { + return { + label: getComponent(entity, NameComponent), + value: getComponent(entity, UUIDComponent) + } + }) + + const onChangeCubemapURLSource = useCallback((value) => { + const directory = value[value.length - 1] === '/' ? value.substring(0, value.length - 1) : value + if (directory !== directory /*envmapComponent.envMapSourceURL*/) { + updateProperties(EnvmapComponent, { envMapSourceURL: directory }) + } + }, []) + + const envmapComponent = useComponent(entity, EnvmapComponent) + + const errors = getEntityErrors(props.entity, EnvmapComponent) + + return ( + } + > + + + + {envmapComponent.type.value === EnvMapSourceType.Color && ( + + + + )} + {envmapComponent.type.value === EnvMapSourceType.Bake && ( + + + + )} + {envmapComponent.type.value === EnvMapSourceType.Texture && ( +
    + + + + + {envmapComponent.envMapTextureType.value === EnvMapTextureType.Cubemap && ( + + )} + {envmapComponent.envMapTextureType.value === EnvMapTextureType.Equirectangular && ( + + )} + {errors?.MISSING_FILE && ( +
    {t('editor:properties.scene.error-url')}
    + )} +
    +
    + )} + + {envmapComponent.type.value !== EnvMapSourceType.None && ( + + + + )} +
    + ) +} +EnvMapEditor.iconComponent = IoMapOutline +export default EnvMapEditor diff --git a/packages/ui/src/components/editor/properties/gallery/index.stories.tsx b/packages/ui/src/components/editor/properties/gallery/index.stories.tsx new file mode 100644 index 00000000000..b0ca058a7fe --- /dev/null +++ b/packages/ui/src/components/editor/properties/gallery/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Gallery', + component: Component, + parameters: { + componentSubtitle: 'GalleryNodeEditor', + jest: 'galleryNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/gallery/index.tsx b/packages/ui/src/components/editor/properties/gallery/index.tsx new file mode 100644 index 00000000000..a7bf87ef148 --- /dev/null +++ b/packages/ui/src/components/editor/properties/gallery/index.tsx @@ -0,0 +1,109 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { EditorComponentType } from '@etherealengine/editor/src/components/properties/Util' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { BsPlusSquare } from 'react-icons/bs' +import { LuImage } from 'react-icons/lu' +import { Quaternion, Vector3 } from 'three' +import Text from '../../../../primitives/tailwind/Text' +import InputGroup from '../../input/Group' +import StringInput from '../../input/String' +import NodeEditor from '../nodeEditor' + +export const GalleryNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + //const spawnComponent = useComponent(props.entity, SpawnPointComponent) + const elements = ['hello', 'bye', 'thanks'] // temp use + + return ( + } + > +
    + + {t('editor:properties.gallery.lbl-thumbnail')} + +
    + { + const elem = { position: new Vector3(), quaternion: new Quaternion() } + const newElements = [ + //...elements.get(NO_PROXY), + ...elements, + elem + ] + //commitProperty(, 'elements')(newElements) + }} + /> +
    +
    + {elements.map( + ( + elem, + index // need styling + ) => ( +
    + + {t('editor:properties.gallery.lbl-asset') + ' ' + (index + 1)} + + + { + //updateProperty(, '') + }} + onRelease={(value) => { + //commitProperty(, '') + }} + /> + + + { + //updateProperty(, '') + }} + onRelease={(value) => { + //commitProperty(, '') + }} + /> + +
    + ) + )} +
    + ) +} + +GalleryNodeEditor.iconComponent = LuImage + +export default GalleryNodeEditor diff --git a/packages/ui/src/components/editor/properties/groundPlane/index.tsx b/packages/ui/src/components/editor/properties/groundPlane/index.tsx new file mode 100644 index 00000000000..a66f72eab01 --- /dev/null +++ b/packages/ui/src/components/editor/properties/groundPlane/index.tsx @@ -0,0 +1,76 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { FaSquareFull } from 'react-icons/fa6' + +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { GroundPlaneComponent } from '@etherealengine/engine/src/scene/components/GroundPlaneComponent' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import ColorInput from '../../../../primitives/tailwind/Color' +import InputGroup from '../../input/Group' +import NodeEditor from '../nodeEditor' + +export const GroundPlaneNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const groundPlaneComponent = useComponent(props.entity, GroundPlaneComponent) + + return ( + + + + + + + + + ) +} + +GroundPlaneNodeEditor.iconComponent = FaSquareFull + +export default GroundPlaneNodeEditor diff --git a/packages/ui/src/components/editor/properties/group/index.stories.tsx b/packages/ui/src/components/editor/properties/group/index.stories.tsx new file mode 100644 index 00000000000..320593ff222 --- /dev/null +++ b/packages/ui/src/components/editor/properties/group/index.stories.tsx @@ -0,0 +1,44 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/group', + component: Component, + parameters: { + componentSubtitle: 'PropertyGroup', + jest: 'propertyGroup.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/properties/group/index.tsx b/packages/ui/src/components/editor/properties/group/index.tsx new file mode 100644 index 00000000000..d613cf1d05c --- /dev/null +++ b/packages/ui/src/components/editor/properties/group/index.tsx @@ -0,0 +1,85 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { Fragment, useState } from 'react' +import { HiOutlineChevronDown, HiOutlineChevronRight } from 'react-icons/hi' +import { HiMiniXMark } from 'react-icons/hi2' +import { PiCursor } from 'react-icons/pi' +import Button from '../../../../primitives/tailwind/Button' +import Text from '../../../../primitives/tailwind/Text' + +interface Props { + name?: string + icon?: any + description?: string + onClose?: () => void + children?: React.ReactNode + rest?: Record +} + +const PropertyGroup = ({ name, icon, description, children, onClose, ...rest }: Props) => { + const [minimized, setMinimized] = useState(false) + + return ( +
    +
    + + )} + {/**/} +
    +
    + {!minimized && description && ( + + {description.split('\\n').map((line, idx) => ( + + {line} +
    +
    + ))} +
    + )} + {!minimized &&
    {children}
    } +
    + ) +} + +PropertyGroup.defaultProps = { + name: 'Component name', + icon: +} + +export default PropertyGroup diff --git a/packages/ui/src/components/editor/properties/image/index.stories.tsx b/packages/ui/src/components/editor/properties/image/index.stories.tsx new file mode 100644 index 00000000000..5f108feb095 --- /dev/null +++ b/packages/ui/src/components/editor/properties/image/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Image', + component: Component, + parameters: { + componentSubtitle: 'ImageNodeEditor', + jest: 'imageNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/properties/image/index.tsx b/packages/ui/src/components/editor/properties/image/index.tsx new file mode 100644 index 00000000000..f59b4502fe1 --- /dev/null +++ b/packages/ui/src/components/editor/properties/image/index.tsx @@ -0,0 +1,73 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { getEntityErrors } from '@etherealengine/engine/src/scene/components/ErrorComponent' +import { ImageComponent } from '@etherealengine/engine/src/scene/components/ImageComponent' + +import { useComponent } from '@etherealengine/ecs' +import { EditorComponentType, commitProperty } from '@etherealengine/editor/src/components/properties/Util' +import { LuImage } from 'react-icons/lu' +import InputGroup from '../../input/Group' +import ImageInput from '../../input/Image' +import NodeEditor from '../nodeEditor' +import ImageSourceProperties from './sourceProperties' + +export const ImageNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const entity = props.entity + const imageComponent = useComponent(entity, ImageComponent) + const errors = getEntityErrors(props.entity, ImageComponent) + + return ( + } + > + + + + {errors ? ( + Object.entries(errors).map(([err, message]) => ( +
    + {'Error: ' + err + '--' + message} +
    + )) + ) : ( + <> + )} + {} + {/**/} +
    + ) +} + +ImageNodeEditor.iconComponent = LuImage + +export default ImageNodeEditor diff --git a/packages/ui/src/components/editor/properties/image/sourceProperties/index.stories.tsx b/packages/ui/src/components/editor/properties/image/sourceProperties/index.stories.tsx new file mode 100644 index 00000000000..a2ff96588e2 --- /dev/null +++ b/packages/ui/src/components/editor/properties/image/sourceProperties/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Image/SourceProperties', + component: Component, + parameters: { + componentSubtitle: 'ImageSourcePropertiesEditor', + jest: 'imageNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: Component.defaultProps } diff --git a/packages/ui/src/components/editor/properties/image/sourceProperties/index.tsx b/packages/ui/src/components/editor/properties/image/sourceProperties/index.tsx new file mode 100644 index 00000000000..02aabbe5b1c --- /dev/null +++ b/packages/ui/src/components/editor/properties/image/sourceProperties/index.tsx @@ -0,0 +1,107 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { BackSide, DoubleSide, FrontSide } from 'three' + +import { ImageAlphaMode, ImageProjection } from '@etherealengine/engine/src/scene/classes/ImageUtils' +import { ImageComponent } from '@etherealengine/engine/src/scene/components/ImageComponent' + +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import SelectInput from '../../../input/Select' + +const mapValue = (v) => ({ label: v, value: v }) +const imageProjectionOptions = Object.values(ImageProjection).map(mapValue) +const imageTransparencyOptions = Object.values(ImageAlphaMode).map(mapValue) + +const ImageProjectionSideOptions = [ + { label: 'Front', value: FrontSide }, + { label: 'Back', value: BackSide }, + { label: 'Both', value: DoubleSide } +] + +export const ImageSourceProperties: EditorComponentType = (props) => { + const { t } = useTranslation() + + //const imageComponent = useComponent(props.entity, ImageComponent) + + return ( + <> + + + + { + /*imageComponent.alphaMode.value === ImageAlphaMode.Mask*/ true && ( + + + + ) + } + + + + + + + + ) +} + +export default ImageSourceProperties diff --git a/packages/ui/src/components/editor/properties/imageGrid/index.stories.tsx b/packages/ui/src/components/editor/properties/imageGrid/index.stories.tsx new file mode 100644 index 00000000000..3a4e1275002 --- /dev/null +++ b/packages/ui/src/components/editor/properties/imageGrid/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/ImageGrid', + component: Component, + parameters: { + componentSubtitle: 'ImageGridNodeEditor', + jest: 'imageGridNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/imageGrid/index.tsx b/packages/ui/src/components/editor/properties/imageGrid/index.tsx new file mode 100644 index 00000000000..a72791c97cc --- /dev/null +++ b/packages/ui/src/components/editor/properties/imageGrid/index.tsx @@ -0,0 +1,106 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { EditorComponentType } from '@etherealengine/editor/src/components/properties/Util' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { BsPlusSquare } from 'react-icons/bs' +import { LuImage } from 'react-icons/lu' +import { Quaternion, Vector3 } from 'three' +import Text from '../../../../primitives/tailwind/Text' +import InputGroup from '../../input/Group' +import StringInput from '../../input/String' +import NodeEditor from '../nodeEditor' + +/** + * SpawnPointNodeEditor component used to provide the editor view to customize Spawn Point properties. + */ +export const GalleryNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + //const spawnComponent = useComponent(props.entity, SpawnPointComponent) + const elements = ['hello', 'bye', 'thanks'] // temp use + + return ( + }> +
    + + {t('editor:properties.gallery.assets')} + +
    + { + const elem = { position: new Vector3(), quaternion: new Quaternion() } + const newElements = [ + //...elements.get(NO_PROXY), + ...elements, + elem + ] + //commitProperty(, 'elements')(newElements) + }} + /> +
    +
    + {elements.map( + ( + elem, + index // need styling + ) => ( +
    + + {t('editor:properties.gallery.lbl-asset') + ' ' + (index + 1)} + + + { + //updateProperty(, '') + }} + onRelease={(value) => { + //commitProperty(, '') + }} + /> + + + { + //updateProperty(, '') + }} + onRelease={(value) => { + //commitProperty(, '') + }} + /> + +
    + ) + )} +
    + ) +} + +GalleryNodeEditor.iconComponent = LuImage + +export default GalleryNodeEditor diff --git a/packages/ui/src/components/editor/properties/light/ambient/index.stories.tsx b/packages/ui/src/components/editor/properties/light/ambient/index.stories.tsx new file mode 100644 index 00000000000..9877ca2f6ee --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/ambient/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/Ambient', + component: Component, + parameters: { + componentSubtitle: 'AmbientLightNodeEditor', + jest: 'ambientLightNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/ambient/index.tsx b/packages/ui/src/components/editor/properties/light/ambient/index.tsx new file mode 100644 index 00000000000..73b173d5e4c --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/ambient/index.tsx @@ -0,0 +1,84 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { AmbientLightComponent } from '@etherealengine/spatial/src/renderer/components/AmbientLightComponent' +import { HiOutlineSun } from 'react-icons/hi2' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import NodeEditor from '../../nodeEditor' + +/** + * AmbientLightNodeEditor component used to customize the ambient light element on the scene + * ambient light is basically used to illuminates all the objects present inside the scene. + */ +export const AmbientLightNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const lightComponent = useComponent(props.entity, AmbientLightComponent) + + return ( + } + > + + + + + + + + ) +} + +AmbientLightNodeEditor.iconComponent = HiOutlineSun + +export default AmbientLightNodeEditor diff --git a/packages/ui/src/components/editor/properties/light/directional/index.stories.tsx b/packages/ui/src/components/editor/properties/light/directional/index.stories.tsx new file mode 100644 index 00000000000..22bc026031d --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/directional/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/Directional', + component: Component, + parameters: { + componentSubtitle: 'DirectionalLightNodeEditor', + jest: 'directionalLightNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/directional/index.tsx b/packages/ui/src/components/editor/properties/light/directional/index.tsx new file mode 100644 index 00000000000..8b1f7a5c359 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/directional/index.tsx @@ -0,0 +1,94 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { DirectionalLightComponent } from '@etherealengine/spatial/src/renderer/components/DirectionalLightComponent' + +import { BsLightning } from 'react-icons/bs' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import NodeEditor from '../../nodeEditor' + +/** + * DirectionalLightNodeEditor is used provides properties to customize DirectionaLight element. + */ +export const DirectionalLightNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const lightComponent = useComponent(props.entity, DirectionalLightComponent).value + + return ( + } + > + + + + + + + {/**/} + + + + + ) +} + +DirectionalLightNodeEditor.iconComponent = BsLightning + +export default DirectionalLightNodeEditor diff --git a/packages/ui/src/components/editor/properties/light/hemisphere/index.stories.tsx b/packages/ui/src/components/editor/properties/light/hemisphere/index.stories.tsx new file mode 100644 index 00000000000..580ba9a4119 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/hemisphere/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/Hemisphere', + component: Component, + parameters: { + componentSubtitle: 'HemisphereLightNodeEditor', + jest: 'hemisphereLightNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/hemisphere/index.tsx b/packages/ui/src/components/editor/properties/light/hemisphere/index.tsx new file mode 100644 index 00000000000..b37f0da2183 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/hemisphere/index.tsx @@ -0,0 +1,85 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { HemisphereLightComponent } from '@etherealengine/spatial/src/renderer/components/HemisphereLightComponent' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { PiSunHorizon } from 'react-icons/pi' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import NodeEditor from '../../nodeEditor' + +/** + * HemisphereLightNodeEditor used to provide property customization view for Hemisphere Light. + */ +export const HemisphereLightNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const lightComponent = useComponent(props.entity, HemisphereLightComponent).value + + return ( + } + > + + + + + + + + + + + ) +} + +HemisphereLightNodeEditor.iconComponent = PiSunHorizon + +export default HemisphereLightNodeEditor diff --git a/packages/ui/src/components/editor/properties/light/point/index.stories.tsx b/packages/ui/src/components/editor/properties/light/point/index.stories.tsx new file mode 100644 index 00000000000..a45c2d89956 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/point/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/Point', + component: Component, + parameters: { + componentSubtitle: 'PointLightNodeEditor', + jest: 'pointLightNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/point/index.tsx b/packages/ui/src/components/editor/properties/light/point/index.tsx new file mode 100644 index 00000000000..26dffc550f3 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/point/index.tsx @@ -0,0 +1,95 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { PointLightComponent } from '@etherealengine/spatial/src/renderer/components/PointLightComponent' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { AiOutlineBulb } from 'react-icons/ai' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import NodeEditor from '../../nodeEditor' +import LightShadowProperties from '../shadowProperties' + +export const PointLightNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const lightComponent = useComponent(props.entity, PointLightComponent).value + + return ( + }> + + + + + + + + + + + + + + + ) +} + +PointLightNodeEditor.iconComponent = AiOutlineBulb + +export default PointLightNodeEditor diff --git a/packages/ui/src/components/editor/properties/light/shadowProperties/index.stories.tsx b/packages/ui/src/components/editor/properties/light/shadowProperties/index.stories.tsx new file mode 100644 index 00000000000..8320b8545e5 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/shadowProperties/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/ShadowProperties', + component: Component, + parameters: { + componentSubtitle: 'LightShadowProperties', + jest: 'LightShadowProperties.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/shadowProperties/index.tsx b/packages/ui/src/components/editor/properties/light/shadowProperties/index.tsx new file mode 100644 index 00000000000..b85b2ec626f --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/shadowProperties/index.tsx @@ -0,0 +1,88 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { useTranslation } from 'react-i18next' + +import { Component, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { Entity } from '@etherealengine/ecs/src/Entity' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import React from 'react' +import BooleanInput from '../../../input/Boolean' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' + +/**creating properties for LightShadowProperties component */ +type LightShadowPropertiesProps = { + entity: Entity + comp: Component +} + +/** + * OnChangeShadowMapResolution used to customize properties of LightShadowProperties + * Used with LightNodeEditors. + */ +export const LightShadowProperties: EditorComponentType = (props: LightShadowPropertiesProps) => { + const { t } = useTranslation() + + const lightComponent = useComponent(props.entity, props.comp).value as any + + return ( + <> + + + + + + + + + + + ) +} + +export default LightShadowProperties diff --git a/packages/ui/src/components/editor/properties/light/spot/index.stories.tsx b/packages/ui/src/components/editor/properties/light/spot/index.stories.tsx new file mode 100644 index 00000000000..cf5546246ea --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/spot/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Light/Spot', + component: Component, + parameters: { + componentSubtitle: 'VideoNodeEditor', + jest: 'videoNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/light/spot/index.tsx b/packages/ui/src/components/editor/properties/light/spot/index.tsx new file mode 100644 index 00000000000..bb4094ec799 --- /dev/null +++ b/packages/ui/src/components/editor/properties/light/spot/index.tsx @@ -0,0 +1,122 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { SpotLightComponent } from '@etherealengine/spatial/src/renderer/components/SpotLightComponent' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { LuCircleDot } from 'react-icons/lu' +import { MathUtils as _Math } from 'three' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import ColorInput from '../../../../../primitives/tailwind/Color' +import InputGroup from '../../../input/Group' +import NumericInput from '../../../input/Numeric' +import NodeEditor from '../../nodeEditor' +import LightShadowProperties from '../shadowProperties' + +/** + * SpotLightNodeEditor component class used to provide editor view for property customization. + */ +export const SpotLightNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const lightComponent = useComponent(props.entity, SpotLightComponent).value + + return ( + }> + + + + + + + + + + + updateProperty(SpotLightComponent, 'angle')(_Math.degToRad(value))} + onRelease={(value) => commitProperty(SpotLightComponent, 'angle')(_Math.degToRad(value))} + unit="°" + /> + + + + + + + + + + ) +} + +SpotLightNodeEditor.iconComponent = LuCircleDot + +export default SpotLightNodeEditor diff --git a/packages/ui/src/components/editor/properties/link/index.stories.tsx b/packages/ui/src/components/editor/properties/link/index.stories.tsx new file mode 100644 index 00000000000..4879081cb1b --- /dev/null +++ b/packages/ui/src/components/editor/properties/link/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Link', + component: Component, + parameters: { + componentSubtitle: 'LinkNodeEditor', + jest: 'linkNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/link/index.tsx b/packages/ui/src/components/editor/properties/link/index.tsx new file mode 100644 index 00000000000..05ae4d251ad --- /dev/null +++ b/packages/ui/src/components/editor/properties/link/index.tsx @@ -0,0 +1,93 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PiLinkBreak } from 'react-icons/pi' + +import { useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { getEntityErrors } from '@etherealengine/engine/src/scene/components/ErrorComponent' +import { LinkComponent } from '@etherealengine/engine/src/scene/components/LinkComponent' +import BooleanInput from '../../input/Boolean' +import InputGroup from '../../input/Group' +import { ControlledStringInput } from '../../input/String' +import NodeEditor from '../nodeEditor' + +/** + * LinkNodeEditor component used to provide the editor view to customize link properties. + */ +export const LinkNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const linkComponent = useComponent(props.entity, LinkComponent) + const errors = getEntityErrors(props.entity, LinkComponent) + + return ( + } + > + {errors + ? Object.entries(errors).map(([err, message]) => ( +
    + {'Error: ' + err + '--' + message} +
    + )) + : null} + + + + + {linkComponent.sceneNav.value ? ( + + + + ) : ( + + + + )} +
    + ) +} + +LinkNodeEditor.iconComponent = PiLinkBreak + +export default LinkNodeEditor diff --git a/packages/ui/src/components/editor/properties/media/index.stories.tsx b/packages/ui/src/components/editor/properties/media/index.stories.tsx new file mode 100644 index 00000000000..1ebacc769ec --- /dev/null +++ b/packages/ui/src/components/editor/properties/media/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Media', + component: Component, + parameters: { + componentSubtitle: 'MediaNodeEditor', + jest: '', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/media/index.tsx b/packages/ui/src/components/editor/properties/media/index.tsx new file mode 100644 index 00000000000..1de9d35286e --- /dev/null +++ b/packages/ui/src/components/editor/properties/media/index.tsx @@ -0,0 +1,172 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { HiOutlineVideoCamera } from 'react-icons/hi2' + +import { useComponent, useOptionalComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { + MediaComponent, + MediaElementComponent, + setTime +} from '@etherealengine/engine/src/scene/components/MediaComponent' +import { PlayMode } from '@etherealengine/engine/src/scene/constants/PlayMode' +import Button from '../../../../primitives/tailwind/Button' +import Slider from '../../../../primitives/tailwind/Slider' +import ArrayInputGroup from '../../input/Array' +import BooleanInput from '../../input/Boolean' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import NodeEditor from '../nodeEditor' + +const PlayModeOptions = [ + { + label: 'Single', + value: PlayMode.single + }, + { + label: 'Random', + value: PlayMode.random + }, + { + label: 'Loop', + value: PlayMode.loop + }, + { + label: 'SingleLoop', + value: PlayMode.singleloop + } +] + +/** + * MediaNodeEditor used to render editor view for property customization. + */ +export const MediaNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const media = useComponent(props.entity, MediaComponent) + const element = useOptionalComponent(props.entity, MediaElementComponent) + + const toggle = () => { + media.paused.set(!media.paused.value) + } + + const reset = () => { + if (element) { + setTime(element.element, media.seekTime.value) + } + } + + return ( + } + > + + commitProperty(MediaComponent, 'volume')} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + {media.resources.length > 0 && ( + + + + + )} + + ) +} + +MediaNodeEditor.iconComponent = HiOutlineVideoCamera + +export default MediaNodeEditor diff --git a/packages/ui/src/components/editor/properties/mesh/geometryEditor.tsx b/packages/ui/src/components/editor/properties/mesh/geometryEditor.tsx new file mode 100644 index 00000000000..8c537dfbcce --- /dev/null +++ b/packages/ui/src/components/editor/properties/mesh/geometryEditor.tsx @@ -0,0 +1,94 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback, useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { BufferAttribute, BufferGeometry, InterleavedBufferAttribute } from 'three' + +import { useHookstate } from '@etherealengine/hyperflux' +import { HiTrash } from 'react-icons/hi2' +import Button from '../../../../primitives/tailwind/Button' +import Label from '../../../../primitives/tailwind/Label' +import Text from '../../../../primitives/tailwind/Text' + +const recalculateNormals = (geometry: BufferGeometry) => { + geometry.computeVertexNormals() +} + +export default function GeometryEditor({ geometry }: { ['geometry']: BufferGeometry | null }) { + if (!geometry) return null + + const { t } = useTranslation() + + const updateGeo = useHookstate(0) + + const updateGeoData = useCallback( + () => ({ + uuid: geometry.uuid, + name: geometry.name, + attributes: Object.entries(geometry.attributes).map(([attribName, attrib]) => ({ + name: attribName, + count: attrib.count, + itemSize: attrib.itemSize, + normalized: (attrib as BufferAttribute | InterleavedBufferAttribute).normalized + })) + }), + [updateGeo] + ) + + const geoData = useHookstate(updateGeoData()) + useEffect(() => { + geoData.set(updateGeoData()) + }, [updateGeo]) + + const deleteBufferAttribute = (attribName: string) => { + geometry.deleteAttribute(attribName) + updateGeo.set(updateGeo.get() + 1) + } + + return ( +
    + + {geoData.attributes.map((attribute, idx) => ( +
    +
    + ))} +
    + ) +} diff --git a/packages/ui/src/components/editor/properties/mesh/index.tsx b/packages/ui/src/components/editor/properties/mesh/index.tsx new file mode 100644 index 00000000000..c800aceacd7 --- /dev/null +++ b/packages/ui/src/components/editor/properties/mesh/index.tsx @@ -0,0 +1,66 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { getComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { Entity } from '@etherealengine/ecs/src/Entity' +import { EditorComponentType } from '@etherealengine/editor/src/components/properties/Util' +import { MeshComponent } from '@etherealengine/spatial/src/renderer/components/MeshComponent' +import { HiMinus, HiPlusSmall } from 'react-icons/hi2' +import Accordion from '../../../../primitives/tailwind/Accordion' +import NodeEditor from '../nodeEditor' +import GeometryEditor from './geometryEditor' + +const MeshNodeEditor: EditorComponentType = (props: { entity: Entity }) => { + const entity = props.entity + const { t } = useTranslation() + const meshComponent = getComponent(entity, MeshComponent) + return ( + + } + shrinkIcon={} + > + + + } + shrinkIcon={} + > + {/* */} + + + ) +} + +export default MeshNodeEditor diff --git a/packages/ui/src/components/editor/properties/mesh/materialEditor.tsx b/packages/ui/src/components/editor/properties/mesh/materialEditor.tsx new file mode 100644 index 00000000000..1cbd7e6a448 --- /dev/null +++ b/packages/ui/src/components/editor/properties/mesh/materialEditor.tsx @@ -0,0 +1,24 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ diff --git a/packages/ui/src/components/editor/properties/mountPoint/index.stories.tsx b/packages/ui/src/components/editor/properties/mountPoint/index.stories.tsx new file mode 100644 index 00000000000..1c9c8aa4e9d --- /dev/null +++ b/packages/ui/src/components/editor/properties/mountPoint/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/MountPoint', + component: Component, + parameters: { + componentSubtitle: 'MountPointNodeEditor', + jest: 'mountPointNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/mountPoint/index.tsx b/packages/ui/src/components/editor/properties/mountPoint/index.tsx new file mode 100644 index 00000000000..e4d6b351303 --- /dev/null +++ b/packages/ui/src/components/editor/properties/mountPoint/index.tsx @@ -0,0 +1,80 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { useComponent } from '@etherealengine/ecs' +import InputGroup from '@etherealengine/editor/src/components/inputs/InputGroup' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { MountPoint, MountPointComponent } from '@etherealengine/engine/src/scene/components/MountPointComponent' +import { LuUsers2 } from 'react-icons/lu' +import SelectInput from '../../input/Select' +import Vector3Input from '../../input/Vector3' +import NodeEditor from '../nodeEditor' + +/** + * MountPointNodeEditor component used to provide the editor view to customize Mount Point properties. + * + * @type {Class component} + */ +export const MountPointNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const mountComponent = useComponent(props.entity, MountPointComponent) + + return ( + } + > + + ({ label: key, value }))} + onChange={commitProperty(MountPointComponent, 'type')} + /> + + + + + + ) +} + +MountPointNodeEditor.iconComponent = LuUsers2 + +export default MountPointNodeEditor diff --git a/packages/ui/src/components/editor/properties/nodeEditor/index.stories.tsx b/packages/ui/src/components/editor/properties/nodeEditor/index.stories.tsx new file mode 100644 index 00000000000..496930f0b5d --- /dev/null +++ b/packages/ui/src/components/editor/properties/nodeEditor/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/NodeEditor', + component: Component, + parameters: { + componentSubtitle: 'NodeEditor', + jest: 'nodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/nodeEditor/index.tsx b/packages/ui/src/components/editor/properties/nodeEditor/index.tsx new file mode 100644 index 00000000000..59de26ede82 --- /dev/null +++ b/packages/ui/src/components/editor/properties/nodeEditor/index.tsx @@ -0,0 +1,110 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { PropsWithChildren, Suspense } from 'react' + +import { LoadingCircle } from '@etherealengine/client-core/src/components/LoadingCircle' +import { hasComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { EditorPropType } from '@etherealengine/editor/src/components/properties/Util' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import Text from '../../../../primitives/tailwind/Text' +import PropertyGroup from '../group' + +interface NodeErrorProps { + name?: string + children?: React.ReactNode +} + +interface NodeErrorState { + error: Error | null +} + +class NodeEditorErrorBoundary extends React.Component { + public state: NodeErrorState = { + error: null + } + + public static getDerivedStateFromError(error: Error): NodeErrorState { + // Update state so the next render will show the fallback UI. + return { error } + } + + public componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('Uncaught error:', error, errorInfo) + } + + public render() { + if (this.state.error) { + return ( +
    + + [{this.props.name}] {this.state.error.message}` + +
    {this.state.error.stack}
    +
    + ) + } + + return this.props.children + } +} + +type NodeEditorProps = EditorPropType & { + description?: string + name?: string + icon?: JSX.Element +} + +export const NodeEditor: React.FC> = ({ + description, + children, + name, + entity, + component, + icon +}) => { + return ( + { + const entities = SelectionState.getSelectedEntities() + EditorControlFunctions.addOrRemoveComponent(entities, component, false) + } + : undefined + } + > + }> + {children} + + + ) +} + +export default NodeEditor diff --git a/packages/ui/src/components/editor/properties/parameter/index.tsx b/packages/ui/src/components/editor/properties/parameter/index.tsx new file mode 100644 index 00000000000..4eb0708f65a --- /dev/null +++ b/packages/ui/src/components/editor/properties/parameter/index.tsx @@ -0,0 +1,162 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { Fragment } from 'react' + +import { generateDefaults } from '@etherealengine/spatial/src/renderer/materials/constants/DefaultArgs' +import ColorInput from '../../../../primitives/tailwind/Color' +import BooleanInput from '../../input/Boolean' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import StringInput from '../../input/String' +import TexturePreviewInput from '../../input/Texture' + +export default function ParameterInput({ + entity, + values, + onChange, + defaults, + thumbnails +}: { + entity: string + values: object + defaults?: object + thumbnails?: Record + onChange: (k: string) => (v) => void +}) { + function setArgsProp(k) { + const thisOnChange = onChange(k) + return (value) => { + //values[k] = value + thisOnChange(value) + } + } + + function setArgsArrayProp(k, idx) { + const thisOnChange = onChange(k) + return (value) => { + const nuVals = values[k].map((oldVal, oldIdx) => (idx === oldIdx ? value : oldVal)) + thisOnChange(nuVals) + } + } + + function setArgsObjectProp(k) { + const thisOnChange = onChange(k) + const oldVal = values[k] + return (field) => { + return (value) => { + const nuVal = Object.fromEntries([ + ...Object.entries(oldVal).filter(([_field, _value]) => _field !== field), + [field, value] + ]) + thisOnChange(nuVal) + } + } + } + + const _defaults = defaults ?? generateDefaults(values) + /* +0: "boolean" +1: "string" +2: "integer" +3: "float" +4: "vec2" +5: "vec3" +6: "vec4" +7: "color" +8: "euler" +9: "quat" +10: "mat3" +11: "mat4" +12: "object" +13: "list" +14: "entity"*/ + return ( + + {Object.entries(_defaults).map(([k, parms]: [string, any]) => { + const compKey = `${entity}-${k}` + return ( + + {(() => { + switch (parms.type) { + case 'boolean': + return + case 'entity': + case 'integer': + case 'float': + return + case 'string': + return + case 'color': + return + case 'texture': + if (thumbnails?.[k]) + return + else return + case 'vec2': + case 'vec3': + case 'vec4': + return ( + + {typeof values[k]?.map === 'function' && + (values[k] as number[]).map((arrayVal, idx) => { + return ( + + ) + })} + + ) + case 'select': + return ( + + ) + case 'object': + return ( + + ) + default: + return <> + } + })()} + + ) + })} + + ) +} diff --git a/packages/ui/src/components/editor/properties/particle/index.stories.tsx b/packages/ui/src/components/editor/properties/particle/index.stories.tsx new file mode 100644 index 00000000000..ff3dbeb489b --- /dev/null +++ b/packages/ui/src/components/editor/properties/particle/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Particle', + component: Component, + parameters: { + componentSubtitle: 'ParticleNodeEditor', + jest: 'particleNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/particle/index.tsx b/packages/ui/src/components/editor/properties/particle/index.tsx new file mode 100644 index 00000000000..364c5030972 --- /dev/null +++ b/packages/ui/src/components/editor/properties/particle/index.tsx @@ -0,0 +1,446 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { + AdditiveBlending, + Blending, + CustomBlending, + MultiplyBlending, + NoBlending, + NormalBlending, + SubtractiveBlending +} from 'three' +import { BurstParameters, RenderMode } from 'three.quarks' + +import { getComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { + ApplyForceBehaviorJSON, + BehaviorJSON, + BurstParametersJSON, + CONE_SHAPE_DEFAULT, + ColorGeneratorJSON, + ConstantColorJSON, + DONUT_SHAPE_DEFAULT, + ExtraSystemJSON, + MESH_SHAPE_DEFAULT, + POINT_SHAPE_DEFAULT, + ParticleSystemComponent, + SPHERE_SHAPE_DEFAULT, + ValueGeneratorJSON +} from '@etherealengine/engine/src/scene/components/ParticleSystemComponent' +import { State } from '@etherealengine/hyperflux' +import { HiSparkles } from 'react-icons/hi' + +import NumericInputGroup from '@etherealengine/editor/src/components/inputs/NumericInputGroup' +import ParameterInput from '@etherealengine/editor/src/components/inputs/ParameterInput' +import PaginatedList from '@etherealengine/editor/src/components/layout/PaginatedList' +import { + EditorComponentType, + commitProperties, + commitProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import { Button } from '@mui/material' +import BehaviorInput from '../../input/Behavior' +import ColorGenerator from '../../input/Generator/Color' +import ValueGenerator from '../../input/Generator/Value' +import InputGroup from '../../input/Group' +import ModelInput from '../../input/Model' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import TexturePreviewInput from '../../input/Texture' +import NodeEditor from '../nodeEditor' + +const ParticleSystemNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const entity = props.entity + const particleSystemState = useComponent(entity, ParticleSystemComponent) + const particleSystem = particleSystemState.value + + const onSetSystemParm = useCallback((field: keyof typeof particleSystem.systemParameters) => { + const parm = particleSystem.systemParameters[field] + return (value: typeof parm) => { + particleSystemState._refresh.set(particleSystem._refresh + 1) + commitProperty(ParticleSystemComponent, ('systemParameters.' + field) as any)(value) + } + }, []) + + const shapeDefaults = { + point: POINT_SHAPE_DEFAULT, + sphere: SPHERE_SHAPE_DEFAULT, + cone: CONE_SHAPE_DEFAULT, + donut: DONUT_SHAPE_DEFAULT, + mesh_surface: MESH_SHAPE_DEFAULT + } + + const onChangeShape = useCallback(() => { + const onSetShape = onSetSystemParm('shape') + return (shape: string) => { + onSetShape(shapeDefaults[shape]) + } + }, []) + + const onChangeShapeParm = useCallback((field: keyof typeof particleSystem.systemParameters.shape) => { + return (value: any) => { + const nuParms = JSON.parse(JSON.stringify(particleSystem.systemParameters.shape)) + nuParms[field] = value + commitProperty(ParticleSystemComponent, 'systemParameters.shape' as any)(nuParms) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + } + }, []) + + const onSetState = useCallback((state: State) => { + return (value: any) => { + state.set(value) + const { systemParameters, behaviorParameters } = JSON.parse( + JSON.stringify(getComponent(entity, ParticleSystemComponent)) + ) + commitProperties( + ParticleSystemComponent, + { + systemParameters, + behaviorParameters + }, + [props.entity] + ) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + } + }, []) + + const onAddBehavior = useCallback(() => { + const nuBehavior: ApplyForceBehaviorJSON = { + type: 'ApplyForce', + direction: [0, 1, 0], + magnitude: { + type: 'ConstantValue', + value: 1 + } + } + particleSystemState.behaviorParameters.set([ + ...JSON.parse(JSON.stringify(particleSystem.behaviorParameters)), + nuBehavior + ]) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + }, []) + + const onRemoveBehavior = useCallback( + (behavior: BehaviorJSON) => () => { + const data = JSON.parse(JSON.stringify(particleSystem.behaviorParameters.filter((b) => b !== behavior))) + commitProperty(ParticleSystemComponent, 'behaviorParameters')(data) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + }, + [] + ) + + const onAddBurst = useCallback(() => { + const nuBurst: BurstParametersJSON = { + time: 0, + count: 0, + cycle: 0, + interval: 0, + probability: 0 + } + const data = [...JSON.parse(JSON.stringify(particleSystem.systemParameters.emissionBursts)), nuBurst] + commitProperty(ParticleSystemComponent, 'systemParameters.emissionBursts' as any)(data) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + }, []) + + const onRemoveBurst = useCallback((burst: State) => { + return () => { + const data = JSON.parse( + JSON.stringify( + particleSystem.systemParameters.emissionBursts.filter((b: any) => b !== (burst.value as BurstParameters)) + ) + ) + commitProperty(ParticleSystemComponent, 'systemParameters.emissionBursts' as any)(data) + particleSystemState._refresh.set((particleSystem._refresh + 1) % 1000) + } + }, []) + + return ( + } + > +

    Options

    + + + + + + + + + + + + + + + + + + + + ) + : [] + } + element={(burst: State) => { + return ( +
    + + + + + + +
    + ) + }} + /> + {particleSystem.systemParameters.shape.type === 'mesh_surface' && ( + + + + )} + {particleSystem.systemParameters.shape.type !== 'mesh_surface' && ( + + )} + + + + + + + + + + + + + + + } + value={particleSystem.systemParameters.startColor as ConstantColorJSON} + onChange={onSetState} + /> + + + + + + + + {particleSystem.systemParameters.renderMode === RenderMode.Trail && ( + <> + + + } + onChange={onSetState} + /> + + + + + + )} + + + + + + + + + + + {typeof particleSystem.systemParameters.startTileIndex === 'number' && ( + <> + + + + )} + {typeof particleSystem.systemParameters.startTileIndex === 'object' && ( + } + value={particleSystem.systemParameters.startTileIndex as ValueGeneratorJSON} + onChange={onSetState} + /> + )} + + + + ).instancingGeometry + )} + /> + + + + + + + + + + +

    Behaviors

    + + ) => { + return ( + <> + + + + ) + }} + /> +
    + ) +} + +ParticleSystemNodeEditor.iconComponent = HiSparkles + +export default ParticleSystemNodeEditor diff --git a/packages/ui/src/components/editor/properties/portal/index.stories.tsx b/packages/ui/src/components/editor/properties/portal/index.stories.tsx new file mode 100644 index 00000000000..29c81b52412 --- /dev/null +++ b/packages/ui/src/components/editor/properties/portal/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Portal', + component: Component, + parameters: { + componentSubtitle: 'PortalNodeEditor', + jest: 'portalNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/portal/index.tsx b/packages/ui/src/components/editor/properties/portal/index.tsx new file mode 100644 index 00000000000..ff0f85da0b4 --- /dev/null +++ b/packages/ui/src/components/editor/properties/portal/index.tsx @@ -0,0 +1,226 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { CiSpeaker } from 'react-icons/ci' +import { Euler, Quaternion, Vector3 } from 'three' + +import { getComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { + PortalComponent, + PortalEffects, + PortalPreviewTypes +} from '@etherealengine/engine/src/scene/components/PortalComponent' + +import { UUIDComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperties, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { bakeEnvmapTexture, uploadCubemapBakeToServer } from '@etherealengine/editor/src/functions/uploadEnvMapBake' +import { imageDataToBlob } from '@etherealengine/engine/src/scene/classes/ImageUtils' +import { useHookstate } from '@etherealengine/hyperflux' +import { TransformComponent } from '@etherealengine/spatial' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import Button from '../../../../primitives/tailwind/Button' +import EulerInput from '../../input/Euler' +import InputGroup from '../../input/Group' +import ImagePreviewInput from '../../input/Image/Preview' +import SelectInput from '../../input/Select' +import StringInput, { ControlledStringInput } from '../../input/String' +import Vector3Input from '../../input/Vector3' +import NodeEditor from '../nodeEditor' + +type PortalOptions = { + label: string + value: string +} + +const rotation = new Quaternion() + +/** + * PortalNodeEditor provides the editor for properties of PortalNode. + */ +export const PortalNodeEditor: EditorComponentType = (props) => { + const state = useHookstate({ + portals: [] as PortalOptions[], + previewImageData: null as ImageData | null, + previewImageURL: '' + }) + + const { t } = useTranslation() + const transformComponent = useComponent(props.entity, TransformComponent) + const portalComponent = useComponent(props.entity, PortalComponent) + + useEffect(() => { + loadPortals() + }, []) + + const updateCubeMapBake = async () => { + const imageData = await bakeEnvmapTexture( + transformComponent.value.position.clone().add(new Vector3(0, 2, 0).multiply(transformComponent.scale.value)) + ) + const blob = await imageDataToBlob(imageData) + state.previewImageData.set(imageData) + state.previewImageURL.set(URL.createObjectURL(blob!)) + } + + const loadPortals = async () => { + const portalsDetail: any[] = [] + try { + portalsDetail + .push + //...((await API.instance.client.service(portalPath).find({ query: { paginate: false } })) as PortalType[]) + () + console.log('portalsDetail', portalsDetail, getComponent(props.entity, UUIDComponent)) + } catch (error) { + throw new Error(error) + } + state.portals.set( + portalsDetail + .filter((portal) => portal.portalEntityId !== getComponent(props.entity, UUIDComponent)) + .map(({ portalEntityId, portalEntityName, sceneName }) => { + return { value: portalEntityId, label: sceneName + ': ' + portalEntityName } + }) + ) + } + + const uploadEnvmap = async () => { + if (!state.previewImageData.value) return + const url = await uploadCubemapBakeToServer(getComponent(props.entity, NameComponent), state.previewImageData.value) + commitProperties(PortalComponent, { previewImageURL: url }, [props.entity]) + } + + const changeSpawnRotation = (value: Euler) => { + rotation.setFromEuler(value) + + commitProperties(PortalComponent, { spawnRotation: rotation }) + } + + const changePreviewType = (val) => { + commitProperties(PortalComponent, { previewType: val }) + loadPortals() + } + + return ( + } {...props}> + + + + + + + + + + + { + return { value: val, label: val } + })} + value={portalComponent.effectType.value} + onChange={commitProperty(PortalComponent, 'effectType')} + /> + + + { + return { value: val, label: val } + })} + value={portalComponent.previewType.value} + onChange={changePreviewType} + /> + + + + + +
    +
    + + +
    +
    +
    + + + + + + + commitProperty(PortalComponent, 'spawnRotation')(getComponent(props.entity, PortalComponent).spawnRotation) + } + /> + +
    + ) +} + +PortalNodeEditor.iconComponent = CiSpeaker + +export default PortalNodeEditor diff --git a/packages/ui/src/components/editor/properties/positionalAudio/index.stories.tsx b/packages/ui/src/components/editor/properties/positionalAudio/index.stories.tsx new file mode 100644 index 00000000000..03a4d86e084 --- /dev/null +++ b/packages/ui/src/components/editor/properties/positionalAudio/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/PositionalAudio', + component: Component, + parameters: { + componentSubtitle: 'PositionalAudioNodeEditor', + jest: 'positionalAudioNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/positionalAudio/index.tsx b/packages/ui/src/components/editor/properties/positionalAudio/index.tsx new file mode 100644 index 00000000000..f66e37a19c4 --- /dev/null +++ b/packages/ui/src/components/editor/properties/positionalAudio/index.tsx @@ -0,0 +1,209 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { PiSpeakerLowLight } from 'react-icons/pi' + +import { hasComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { PositionalAudioComponent } from '@etherealengine/engine/src/audio/components/PositionalAudioComponent' +import { DistanceModel, DistanceModelOptions } from '@etherealengine/engine/src/audio/constants/AudioConstants' + +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { MediaComponent } from '@etherealengine/engine/src/scene/components/MediaComponent' +import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' +import Slider from '../../../../primitives/tailwind/Slider' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import ProgressBar from '../../input/Progress' +import SelectInput from '../../input/Select' +import NodeEditor from '../nodeEditor' + +/** + * AudioNodeEditor used to customize audio element on the scene. + */ +export const PositionalAudioNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const audioComponent = useComponent(props.entity, PositionalAudioComponent) + + useEffect(() => { + if (!hasComponent(props.entity, MediaComponent) && !hasComponent(props.entity, VolumetricComponent)) { + const nodes = SelectionState.getSelectedEntities() + EditorControlFunctions.addOrRemoveComponent(nodes, MediaComponent, true) + } + }, []) + + return ( + } + > + + + + + + {audioComponent.distanceModel.value === DistanceModel.Linear ? ( + + + + ) : ( + + + + )} + + + + + + + + + + + + + + + + + ) +} + +PositionalAudioNodeEditor.iconComponent = PiSpeakerLowLight + +export default PositionalAudioNodeEditor diff --git a/packages/ui/src/components/editor/properties/rigidBody/index.tsx b/packages/ui/src/components/editor/properties/rigidBody/index.tsx new file mode 100644 index 00000000000..6340cf47785 --- /dev/null +++ b/packages/ui/src/components/editor/properties/rigidBody/index.tsx @@ -0,0 +1,67 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { MdPanTool } from 'react-icons/md' + +import { camelCaseToSpacedString } from '@etherealengine/common/src/utils/camelCaseToSpacedString' +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { EditorComponentType, commitProperty } from '@etherealengine/editor/src/components/properties/Util' +import { RigidBodyComponent } from '@etherealengine/spatial/src/physics/components/RigidBodyComponent' +import { BodyTypes } from '@etherealengine/spatial/src/physics/types/PhysicsTypes' +import Select from '../../../../primitives/tailwind/Select' +import InputGroup from '../../input/Group' +import NodeEditor from '../nodeEditor' + +const bodyTypeOptions = Object.entries(BodyTypes).map(([label, value]) => { + return { label: camelCaseToSpacedString(label as string), value } +}) + +export const RigidBodyComponentEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const rigidbodyComponent = useComponent(props.entity, RigidBodyComponent) + + return ( + + + onChange(parseFloat(event.target.value))} + /> + + {'rem'} + +
    + ) +} + +export const TextBoxEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const text = useComponent(props.entity, TextComponent) + + return ( + + + + + + + + + {}} unit="em" /> + + + {}} unit="px" /> + + + + + + {}} unit="rem" /> + + + + + + {}} unit="pc" /> + + +
    +
    +
    + {}} borderStyles={{ borderTop: '1px solid white' }} /> +
    +
    + {}} borderStyles={{ borderBottom: '1px solid white' }} /> +
    +
    +
    +
    + {}} borderStyles={{ borderLeft: '1px solid white' }} /> +
    +
    + {}} borderStyles={{ borderRight: '1px solid white' }} /> +
    +
    +
    +
    +
    + ) +} + +TextBoxEditor.iconComponent = StreetviewIcon + +export default TextBoxEditor diff --git a/packages/ui/src/components/editor/properties/text/index.stories.tsx b/packages/ui/src/components/editor/properties/text/index.stories.tsx new file mode 100644 index 00000000000..2b2fbe5f730 --- /dev/null +++ b/packages/ui/src/components/editor/properties/text/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Text', + component: Component, + parameters: { + componentSubtitle: 'TextNodeEditor', + jest: 'textNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/text/index.tsx b/packages/ui/src/components/editor/properties/text/index.tsx new file mode 100644 index 00000000000..bffaa4c96cc --- /dev/null +++ b/packages/ui/src/components/editor/properties/text/index.tsx @@ -0,0 +1,511 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +/** + * @fileoverview + * Defines the {@link NodeEditor} UI for managing {@link TextComponent}s in the Studio. + */ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PiTextT } from 'react-icons/pi' + +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties//Util' +import { + FontMaterialKind, + TextComponent, + TroikaTextLineHeight +} from '@etherealengine/engine/src/scene/components/TextComponent' +import { useHookstate } from '@etherealengine/hyperflux' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import { ColorInput } from '../../../../primitives/tailwind/Color' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import SelectInput from '../../input/Select' +import { ControlledStringInput } from '../../input/String' +import Vector2Input from '../../input/Vector2' +import NodeEditor from '../nodeEditor' + +/** + * @description SelectInput option groups for the TextNodeEditor UI tsx code. + * @private Stored `@local` scope of the file, so it only exists once and its not GC'ed when the component is used. + */ +const SelectOptions = { + TextDirection: [ + { label: 'Auto', value: 'auto' }, + { label: 'Left to Right', value: 'ltr' }, + { label: 'Right to Left', value: 'rtl' } + ], + TextAlignment: [ + { label: 'Justify', value: 'justify' }, + { label: 'Center', value: 'center' }, + { label: 'Left', value: 'left' }, + { label: 'Right', value: 'right' } + ], + TextWrapping: [ + { label: 'Whitespace', value: 'normal' }, + { label: 'Break Word', value: 'break-word' } + ], + FontMaterial: [ + { label: 'Basic', value: FontMaterialKind.Basic }, + { label: 'Standard', value: FontMaterialKind.Standard } + ] +} + +/** + * @description Default fallback value for when when text.lineheight is not set to 'normal' + */ +const LineHeightNumericDefault = 1.2 as TroikaTextLineHeight + +const HoverInfo = { + FontFamily: ` +URL of a custom font to be used. Font files can be in .ttf, .otf, or .woff (not .woff2) formats. Defaults to Noto Sans when empty. +Example: https://fonts.gstatic.com/s/orbitron/v9/yMJRMIlzdpvBhQQL_Qq7dys.woff +`, + AdvancedGroup: ` +Toggle Advanced options. Only modify these if you know what you are doing. +`, + TextOrientation: ` +Defines the axis plane on which the text should be laid out when the mesh has no extra rotation transform. +It is specified as a string with two axes: + 1. the horizontal axis with positive pointing right, + 2. the vertical axis with positive pointing up. +Defaults to '+x+y', meaning the text sits on the xy plane with the text's top toward positive y and facing positive z. +A value of '+x-z' would place it on the xz plane with the text's top toward negative z and facing positive y. +`, + Clipping: ` +Limit the range of pixels to draw to the given clip Rectangle. +Directly tied to the axis selection @textOrientation. +Useful for when text wrapping is disabled, but text should still be contained within a certain range. +`, + GlyphResolution: ` +Level of quality at which font glyphs are rasterized. +Compare values 2 and 3 to understand how this value behaves. +A value of 4 is already acceptable quality depending on context. A value of 6 is great quality, very difficult to distinguish from 7. +Anything above 9-10 could literally halt your computer while the text is being rendered. +`, + GlyphDetail: ` +Number of subdivisions of the plane Mesh where the text is being rendered. Useful for material effects. +`, + GPUAccelerated: ` +Forces the text rendering to happen on the CPU when disabled. +Text rendering performance is significantly slower when disabled. +Only useful for hardware that has no GPU acceleration support. +` +} + +/** + * TextNodeEditor component used to provide the editor a view to customize text properties. + */ +export const TextNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + const text = useComponent(props.entity, TextComponent) + const advancedActive = useHookstate(false) // State tracking whether the Advanced Options Section is active or not + + // LineHeight state management + const lineHeightIsNormal = useHookstate(true) // true when `text.lineHeight` is set to its union 'normal' + const lineHeight_setNormal = (checkboxValue: boolean) => { + // Used as a BooleanInput callback for setting the value of lineheight. + // Sets the value to either its 'normal' type-union option, or to a default lineHeight value when the checkbox is off. + lineHeightIsNormal.set(checkboxValue) + if (checkboxValue) text.lineHeight.set('normal' as TroikaTextLineHeight) + else text.lineHeight.set(LineHeightNumericDefault) + } + + return ( + }> + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +

    + +
    + + + + + + + + + + + + +
    +
    +

    + +
    + + + + + + + + + + + + + + + +
    +
    +

    + +
    + + + + + + + + + +
    +
    +

    + + + + {advancedActive.value ? ( + /*Show Advanced Options only when Active*/ + +
    + + + + + + + + + + + + + + + + + + + + + +
    +
    + ) : ( + <>{/*advanced.inactive*/} + )} +
    + ) +} + +TextNodeEditor.iconComponent = PiTextT + +export default TextNodeEditor diff --git a/packages/ui/src/components/editor/properties/transform/index.stories.tsx b/packages/ui/src/components/editor/properties/transform/index.stories.tsx new file mode 100644 index 00000000000..331f0b8ff80 --- /dev/null +++ b/packages/ui/src/components/editor/properties/transform/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Transform', + component: Component, + parameters: { + componentSubtitle: 'TransformNodeEditor', + jest: 'transformNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/transform/index.tsx b/packages/ui/src/components/editor/properties/transform/index.tsx new file mode 100644 index 00000000000..7a0c69e1e13 --- /dev/null +++ b/packages/ui/src/components/editor/properties/transform/index.tsx @@ -0,0 +1,155 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' +import { Euler, Quaternion, Vector3 } from 'three' + +import { + getComponent, + hasComponent, + useComponent, + useOptionalComponent +} from '@etherealengine/ecs/src/ComponentFunctions' +import { SceneDynamicLoadTagComponent } from '@etherealengine/engine/src/scene/components/SceneDynamicLoadTagComponent' +import { getMutableState, useHookstate } from '@etherealengine/hyperflux' + +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import ThreeDRotationIcon from '@mui/icons-material/ThreeDRotation' + +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { ObjectGridSnapState } from '@etherealengine/editor/src/systems/ObjectGridSnapSystem' + +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { EditorHelperState } from '@etherealengine/editor/src/services/EditorHelperState' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import { TransformSpace } from '@etherealengine/engine/src/scene/constants/transformConstants' +import { TransformComponent } from '@etherealengine/spatial' +import EulerInput from '../../input/Euler' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import Vector3Input from '../../input/Vector3' +import PropertyGroup from '../group' + +const position = new Vector3() +const rotation = new Quaternion() +const scale = new Vector3() + +/** + * TransformPropertyGroup component is used to render editor view to customize properties. + */ +export const TransformPropertyGroup: EditorComponentType = (props) => { + const { t } = useTranslation() + + useOptionalComponent(props.entity, SceneDynamicLoadTagComponent) + const transformComponent = useComponent(props.entity, TransformComponent) + const transformSpace = useHookstate(getMutableState(EditorHelperState).transformSpace) + + transformSpace.value === TransformSpace.world + ? transformComponent.matrixWorld.value.decompose(position, rotation, scale) + : transformComponent.matrix.value.decompose(position, rotation, scale) + + scale.copy(transformComponent.scale.value) + + const onRelease = () => { + const bboxSnapState = getMutableState(ObjectGridSnapState) + if (bboxSnapState.enabled.value) { + bboxSnapState.apply.set(true) + } else { + EditorControlFunctions.commitTransformSave([props.entity]) + } + } + + const onChangeDynamicLoad = (value) => { + const selectedEntities = SelectionState.getSelectedEntities() + EditorControlFunctions.addOrRemoveComponent(selectedEntities, SceneDynamicLoadTagComponent, value) + } + + const onChangePosition = (value: Vector3) => { + const selectedEntities = SelectionState.getSelectedEntities() + EditorControlFunctions.positionObject(selectedEntities, [value]) + } + + const onChangeRotation = (value: Euler) => { + const selectedEntities = SelectionState.getSelectedEntities() + EditorControlFunctions.rotateObject(selectedEntities, [value]) + } + + const onChangeScale = (value: Vector3) => { + const selectedEntities = SelectionState.getSelectedEntities() + EditorControlFunctions.scaleObject(selectedEntities, [value], true) + } + + return ( + + + + {hasComponent(props.entity, SceneDynamicLoadTagComponent) && ( + + )} + + + + + + + + + + + + ) +} + +TransformPropertyGroup.iconComponent = ThreeDRotationIcon + +export default TransformPropertyGroup diff --git a/packages/ui/src/components/editor/properties/trigger/index.stories.tsx b/packages/ui/src/components/editor/properties/trigger/index.stories.tsx new file mode 100644 index 00000000000..149933d17af --- /dev/null +++ b/packages/ui/src/components/editor/properties/trigger/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/TriggerProperties', + component: Component, + parameters: { + componentSubtitle: 'TriggerPropertiesNodeEditor', + jest: 'TriggerPropertiesNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/trigger/index.tsx b/packages/ui/src/components/editor/properties/trigger/index.tsx new file mode 100644 index 00000000000..e33a992a592 --- /dev/null +++ b/packages/ui/src/components/editor/properties/trigger/index.tsx @@ -0,0 +1,171 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { UUIDComponent, defineQuery, getComponent, hasComponent, useComponent } from '@etherealengine/ecs' +import { + EditorComponentType, + commitProperties, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { useHookstate } from '@etherealengine/hyperflux' +import { CallbackComponent } from '@etherealengine/spatial/src/common/CallbackComponent' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' +import { TriggerComponent } from '@etherealengine/spatial/src/physics/components/TriggerComponent' +import { EntityTreeComponent } from '@etherealengine/spatial/src/transform/components/EntityTree' +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { HiPlus, HiTrash } from 'react-icons/hi2' +import Button from '../../../../primitives/tailwind/Button' +import { SelectOptionsType } from '../../../../primitives/tailwind/Select' +import InputGroup from '../../input/Group' +import SelectInput from '../../input/Select' +import StringInput from '../../input/String' +import NodeEditor from '../nodeEditor' + +const callbackQuery = defineQuery([CallbackComponent]) + +type TargetOptionType = { label: string; value: string; callbacks: SelectOptionsType[] } + +const TriggerProperties: EditorComponentType = (props) => { + const { t } = useTranslation() + const targets = useHookstate([{ label: 'Self', value: 'Self', callbacks: [] }]) + + const triggerComponent = useComponent(props.entity, TriggerComponent) + + useEffect(() => { + const options = [] as TargetOptionType[] + options.push({ + label: 'Self', + value: 'Self', + callbacks: [] + }) + for (const entity of callbackQuery()) { + if (entity === props.entity || !hasComponent(entity, EntityTreeComponent)) continue + const callbacks = getComponent(entity, CallbackComponent) + options.push({ + label: getComponent(entity, NameComponent), + value: getComponent(entity, UUIDComponent), + callbacks: Object.keys(callbacks).map((cb) => ({ label: cb, value: cb })) + }) + } + targets.set(options) + }, []) + + return ( + +
    +
    + {triggerComponent.triggers.map((trigger, index) => { + const targetOption = targets.value.find((o) => o.value === trigger.target.value) + const target = targetOption ? targetOption.value : 'Self' + console.log('debug1 ', targetOption, 'and', targetOption?.callbacks.length) + return ( +
    +
    + ) + })} +
    + ) +} + +export default TriggerProperties diff --git a/packages/ui/src/components/editor/properties/video/index.stories.tsx b/packages/ui/src/components/editor/properties/video/index.stories.tsx new file mode 100644 index 00000000000..62f0d943299 --- /dev/null +++ b/packages/ui/src/components/editor/properties/video/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Video', + component: Component, + parameters: { + componentSubtitle: 'VideoNodeEditor', + jest: 'videoNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/video/index.tsx b/packages/ui/src/components/editor/properties/video/index.tsx new file mode 100644 index 00000000000..1695ac1fb6e --- /dev/null +++ b/packages/ui/src/components/editor/properties/video/index.tsx @@ -0,0 +1,139 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { HiOutlineVideoCamera } from 'react-icons/hi2' + +import { EntityUUID, UUIDComponent } from '@etherealengine/ecs' +import { getComponent, hasComponent, useComponent } from '@etherealengine/ecs/src/ComponentFunctions' +import { MediaComponent } from '@etherealengine/engine/src/scene/components/MediaComponent' +import { VideoComponent } from '@etherealengine/engine/src/scene/components/VideoComponent' +import { NameComponent } from '@etherealengine/spatial/src/common/NameComponent' + +import { useQuery } from '@etherealengine/ecs/src/QueryFunctions' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { EditorControlFunctions } from '@etherealengine/editor/src/functions/EditorControlFunctions' +import { SelectionState } from '@etherealengine/editor/src/services/SelectionServices' +import InputGroup from '../../input/Group' +import ProgressBar from '../../input/Progress' +import SelectInput from '../../input/Select' +import Vector2Input from '../../input/Vector2' +import NodeEditor from '../nodeEditor' + +const fitOptions = [ + { label: 'Cover', value: 'cover' }, + { label: 'Contain', value: 'contain' }, + { label: 'Vertical', value: 'vertical' }, + { label: 'Horizontal', value: 'horizontal' } +] + +const projectionOptions = [ + { label: 'Flat', value: 'Flat' }, + { label: 'Equirectangular360', value: 'Equirectangular360' } +] + +/** + * VideoNodeEditor used to render editor view for property customization. + */ +export const VideoNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const video = useComponent(props.entity, VideoComponent) + + const mediaEntities = useQuery([MediaComponent]) + + const mediaOptions = mediaEntities + .filter((entity) => entity !== props.entity) + .map((entity) => { + return { label: getComponent(entity, NameComponent), value: getComponent(entity, UUIDComponent) } + }) + mediaOptions.unshift({ label: 'Self', value: '' as EntityUUID }) + + useEffect(() => { + if (!hasComponent(props.entity, MediaComponent)) { + const nodes = SelectionState.getSelectedEntities() + EditorControlFunctions.addOrRemoveComponent(nodes, MediaComponent, true) + } + }, []) + + return ( + } + > + + + + + + + + + + + + + + + + + + ) +} + +VideoNodeEditor.iconComponent = HiOutlineVideoCamera + +export default VideoNodeEditor diff --git a/packages/ui/src/components/editor/properties/visualScript/index.stories.tsx b/packages/ui/src/components/editor/properties/visualScript/index.stories.tsx new file mode 100644 index 00000000000..353d93b086e --- /dev/null +++ b/packages/ui/src/components/editor/properties/visualScript/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/VisualScript', + component: Component, + parameters: { + componentSubtitle: 'VisualScriptNodeEditor', + jest: 'visualScriptNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/visualScript/index.tsx b/packages/ui/src/components/editor/properties/visualScript/index.tsx new file mode 100644 index 00000000000..714753be0ea --- /dev/null +++ b/packages/ui/src/components/editor/properties/visualScript/index.tsx @@ -0,0 +1,68 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import { useComponent } from '@etherealengine/ecs/src/ComponentFunctions' + +import IntegrationInstructionsIcon from '@mui/icons-material/IntegrationInstructions' + +import { EditorComponentType, commitProperty } from '@etherealengine/editor/src/components/properties/Util' +import { VisualScriptComponent } from '@etherealengine/engine' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import InputGroup from '../../input/Group' +import { NodeEditor } from '../nodeEditor' + +/** + * + * AmbientLightNodeEditor component used to customize the ambient light element on the scene + * ambient light is basically used to illuminates all the objects present inside the scene. + * + * @type {[component class]} + */ +export const VisualScriptNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const visualScriptComponent = useComponent(props.entity, VisualScriptComponent) + + return ( + + + + + + + + + ) +} + +VisualScriptNodeEditor.iconComponent = IntegrationInstructionsIcon + +export default VisualScriptNodeEditor diff --git a/packages/ui/src/components/editor/properties/volumetric/index.stories.tsx b/packages/ui/src/components/editor/properties/volumetric/index.stories.tsx new file mode 100644 index 00000000000..ac94545e61f --- /dev/null +++ b/packages/ui/src/components/editor/properties/volumetric/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/Volumetric', + component: Component, + parameters: { + componentSubtitle: 'VolumetricNodeEditor', + jest: '', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/volumetric/index.tsx b/packages/ui/src/components/editor/properties/volumetric/index.tsx new file mode 100644 index 00000000000..8e2d6986de8 --- /dev/null +++ b/packages/ui/src/components/editor/properties/volumetric/index.tsx @@ -0,0 +1,385 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' + +import { + getOptionalMutableComponent, + hasComponent, + useComponent, + useOptionalComponent +} from '@etherealengine/ecs/src/ComponentFunctions' +import { VolumetricComponent } from '@etherealengine/engine/src/scene/components/VolumetricComponent' +import { PlayMode } from '@etherealengine/engine/src/scene/constants/PlayMode' + +import { ECSState } from '@etherealengine/ecs/src/ECSState' +import { Entity } from '@etherealengine/ecs/src/Entity' +import CompoundNumericInput from '@etherealengine/editor/src/components/inputs/CompoundNumericInput' +import { + EditorComponentType, + commitProperty, + updateProperty +} from '@etherealengine/editor/src/components/properties/Util' +import { UVOL1Component } from '@etherealengine/engine/src/scene/components/UVOL1Component' +import { UVOL2Component } from '@etherealengine/engine/src/scene/components/UVOL2Component' +import { TextureType } from '@etherealengine/engine/src/scene/constants/UVOLTypes' +import { getState } from '@etherealengine/hyperflux/functions/StateFunctions' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import VideocamIcon from '@mui/icons-material/Videocam' +import { Button } from '@mui/material' +import { Scrubber } from 'react-scrubber' +import 'react-scrubber/lib/scrubber.css' +import ArrayInputGroup from '../../input/Array' +import InputGroup from '../../input/Group' +import SelectInput from '../../input/Select' +import NodeEditor from '../nodeEditor' + +const PlayModeOptions = [ + { + label: 'Single', + value: PlayMode.single + }, + { + label: 'Random', + value: PlayMode.random + }, + { + label: 'Loop', + value: PlayMode.loop + }, + { + label: 'SingleLoop', + value: PlayMode.singleloop + } +] + +type TextureTargetLabelsType = { + [key in TextureType]: { + label: string + value: number + }[] +} + +/** + * VolumetricNodeEditor provides the editor view to customize properties. + * + * @param {any} props + * @constructor + */ +export const VolumetricNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + const volumetricComponent = useComponent(props.entity, VolumetricComponent) + + const toggle = () => { + volumetricComponent.paused.set(!volumetricComponent.paused.value) + } + + const [trackLabels, setTrackLabels] = React.useState( + [] as { + label: string + value: number + }[] + ) + + useEffect(() => { + const tracks = volumetricComponent.paths.value + if (tracks.length === 0) { + return + } + if (tracks.length === 1) { + const segments = tracks[0].split('/') + setTrackLabels([ + { + label: segments[segments.length - 1], + value: 0 + } + ]) + console.log('Setting labels: ', [ + { + label: segments[segments.length - 1], + value: 0 + } + ]) + return + } + + let prefix = tracks[0] + + // Don't show the longest common prefix + for (let j = 1; j < tracks.length; j++) { + while (tracks[j].indexOf(prefix) !== 0) { + prefix = prefix.substring(0, prefix.length - 1) + } + } + const _trackLabels = [] as { + label: string + value: number + }[] + + for (let i = 0; i < tracks.length; i++) { + _trackLabels.push({ + label: tracks[i].slice(prefix.length), + value: i + }) + } + setTrackLabels(_trackLabels) + console.log('Setting labels: ', _trackLabels) + }, [volumetricComponent.paths]) + + const uvol2 = useOptionalComponent(props.entity, UVOL2Component) + const [geometryTargets, setGeometryTargets] = React.useState( + [] as { + label: string + value: number + }[] + ) + + useEffect(() => { + if (uvol2) { + const _geometryTargets = [] as { + label: string + value: number + }[] + _geometryTargets.push({ + label: 'auto', + value: -1 + }) + uvol2.geometryInfo.targets.value.forEach((target, index) => { + _geometryTargets.push({ + label: target, + value: index + }) + }) + setGeometryTargets(_geometryTargets) + } + }, [uvol2?.geometryInfo.targets]) + + const [textureTargets, setTextureTargets] = React.useState({} as TextureTargetLabelsType) + useEffect(() => { + if (!uvol2) { + return + } + const textureTypes = uvol2.textureInfo.textureTypes.value + const _textureTargets = {} as TextureTargetLabelsType + textureTypes.forEach((textureType) => { + _textureTargets[textureType] = [] as { + label: string + value: number + }[] + _textureTargets[textureType].push({ + label: 'auto', + value: -1 + }) + uvol2.textureInfo[textureType].targets.value.forEach((target, index) => { + _textureTargets[textureType].push({ + label: target, + value: index + }) + }) + }) + setTextureTargets(_textureTargets) + }, [ + uvol2?.textureInfo.textureTypes, + uvol2?.textureInfo.baseColor.targets, + uvol2?.textureInfo.normal.targets, + uvol2?.textureInfo.emissive.targets, + uvol2?.textureInfo.metallicRoughness.targets, + uvol2?.textureInfo.occlusion.targets + ]) + + return ( + + + + + + + + + + + + + + + + {(hasComponent(props.entity, UVOL2Component) || hasComponent(props.entity, UVOL1Component)) && ( + + )} + + + { + volumetricComponent.currentTrackInfo.playbackRate.set(value) + }} + /> + + + + + {volumetricComponent.paths && volumetricComponent.paths.length > 0 && volumetricComponent.paths[0] && ( + + )} + + + {hasComponent(props.entity, UVOL2Component) && ( + <> + + { + if (uvol2) { + uvol2.geometryInfo.userTarget.set(value) + } + }} + /> + + {Object.keys(textureTargets).map((textureType, index) => { + return ( + + { + if (uvol2) { + uvol2?.textureInfo[textureType].userTarget.set(value) + } + }} + /> + + ) + })} + + )} + + + { + volumetricComponent.track.set(value) + }} + /> + + + ) +} + +function VolumetricCurrentTimeScrubber(props: { entity: Entity }) { + const { t } = useTranslation() + const volumetricComponent = useComponent(props.entity, VolumetricComponent) + const uvol2Component = useOptionalComponent(props.entity, UVOL2Component) + + const [isChanging, setIsChanging] = React.useState(false) + + return ( + + { + setIsChanging(true) + }} + onScrubEnd={(value) => { + if (!isChanging) return + const uvol2Component = getOptionalMutableComponent(props.entity, UVOL2Component) + if ( + uvol2Component && + volumetricComponent.currentTrackInfo.currentTime.value < value && + value < uvol2Component.bufferedUntil.value + ) { + const engineState = getState(ECSState) + UVOL2Component.setStartAndPlaybackTime(props.entity, value, engineState.elapsedSeconds) + } + setIsChanging(false) + }} + onScrubChange={() => {}} + tooltip={{ + enabledOnHover: true + }} + {...(hasComponent(props.entity, UVOL2Component) + ? { + bufferPosition: uvol2Component?.bufferedUntil.value + } + : {})} + /> + + ) +} + +//setting iconComponent with icon name +VolumetricNodeEditor.iconComponent = VideocamIcon + +export default VolumetricNodeEditor diff --git a/packages/ui/src/components/editor/properties/xruiPlayback/index.stories.tsx b/packages/ui/src/components/editor/properties/xruiPlayback/index.stories.tsx new file mode 100644 index 00000000000..a1ae1489b24 --- /dev/null +++ b/packages/ui/src/components/editor/properties/xruiPlayback/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Properties/XRUIPlayback', + component: Component, + parameters: { + componentSubtitle: 'XRUIPlaybackNodeEditor', + jest: 'xruiPlaybackNodeEditor.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} +export const Default = { args: {} } diff --git a/packages/ui/src/components/editor/properties/xruiPlayback/index.tsx b/packages/ui/src/components/editor/properties/xruiPlayback/index.tsx new file mode 100644 index 00000000000..b414e644806 --- /dev/null +++ b/packages/ui/src/components/editor/properties/xruiPlayback/index.tsx @@ -0,0 +1,75 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { EditorComponentType } from '@etherealengine/editor/src/components/properties/Util' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { LuPlayCircle } from 'react-icons/lu' +import InputGroup from '../../input/Group' +import NumericInput from '../../input/Numeric' +import NodeEditor from '../nodeEditor' + +/** + * SpawnPointNodeEditor component used to provide the editor view to customize Spawn Point properties. + * + * @type {Class component} + */ +export const XRUIPlaybackNodeEditor: EditorComponentType = (props) => { + const { t } = useTranslation() + + //const spawnComponent = useComponent(props.entity, SpawnPointComponent) + + return ( + } + {...props} + > + + {}} + //onChange={updateProperty(, '')} + //onRelease={commitProperty(, '')} + unit="px" + /> + + + ) +} + +XRUIPlaybackNodeEditor.iconComponent = LuPlayCircle + +export default XRUIPlaybackNodeEditor diff --git a/packages/ui/src/components/editor/settings/import/index.tsx b/packages/ui/src/components/editor/settings/import/index.tsx new file mode 100644 index 00000000000..ff0028d7f41 --- /dev/null +++ b/packages/ui/src/components/editor/settings/import/index.tsx @@ -0,0 +1,336 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { ImportSettingsState } from '@etherealengine/editor/src/components/assets/ImportSettingsPanel' +import { LODList, LODVariantDescriptor } from '@etherealengine/editor/src/constants/GLTFPresets' +import { NO_PROXY, getMutableState, useHookstate } from '@etherealengine/hyperflux' +import { BooleanInput } from '@etherealengine/ui/src/components/editor/input/Boolean' +import { t } from 'i18next' +import React, { useEffect, useState } from 'react' +import Modal from '../../../../primitives/tailwind/Modal' +import Slider from '../../../../primitives/tailwind/Slider' +import InputGroup from '../../input/Group' +import SelectInput from '../../input/Select' +import StringInput from '../../input/String' + +const UASTCFlagOptions = [ + { label: 'Fastest', value: 0 }, + { label: 'Faster', value: 1 }, + { label: 'Default', value: 2 }, + { label: 'Slower', value: 3 }, + { label: 'Very Slow', value: 4 }, + { label: 'Mask', value: 0xf }, + { label: 'UASTC Error', value: 8 }, + { label: 'BC7 Error', value: 16 }, + { label: 'Faster Hints', value: 64 }, + { label: 'Fastest Hints', value: 128 }, + { label: 'Disable Flip and Individual', value: 256 } +] + +const ImageCompressionBox = ({ compressProperties }) => { + return ( + <> + {/* + {t('editor:properties.model.transform.compress') as string} + */} + + compressProperties.mode.set(val)} + /> + + + + + + + + + + + + + + {compressProperties.mode.value === 'ETC1S' && ( + <> + + + + + + + + )} + {compressProperties.mode.value === 'UASTC' && ( + <> + + compressProperties.uastcFlags.set(val)} + /> + + + + + + )} + + ) +} + +export function ImportSettingsPanel() { + const importSettingsState = useHookstate(getMutableState(ImportSettingsState)) + const compressProperties = useHookstate(getMutableState(ImportSettingsState).imageSettings.get(NO_PROXY)) + + const [defaultImportFolder, setDefaultImportFolder] = useState(importSettingsState.importFolder.value) + const [LODImportFolder, setLODImportFolder] = useState(importSettingsState.LODFolder.value) + const [LODGenEnabled, setLODGenEnabled] = useState(importSettingsState.LODsEnabled.value) + const [selectedLODS, setSelectedLods] = useState( + importSettingsState.selectedLODS.get(NO_PROXY) as LODVariantDescriptor[] + ) + const [currentLOD, setCurrentLOD] = useState( + importSettingsState.selectedLODS[0].get(NO_PROXY) as LODVariantDescriptor + ) + const [currentIndex, setCurrentIndex] = useState(0) + const [KTXEnabled, setKTXEnabled] = useState(importSettingsState.imageCompression.value) + + const presetLabels = ['LOD0', 'LOD1', 'LOD2'] + + useEffect(() => { + handleLODChange() + }, [currentLOD, currentIndex]) + + const handleLODChange = () => { + const newLODS = [...selectedLODS] + newLODS.splice(currentIndex, 1, currentLOD) + setSelectedLods(newLODS) + } + + const handleSaveChanges = () => { + importSettingsState.importFolder.set(defaultImportFolder) + importSettingsState.LODFolder.set(LODImportFolder) + importSettingsState.LODsEnabled.set(LODGenEnabled) + importSettingsState.imageCompression.set(KTXEnabled) + importSettingsState.imageSettings.set(compressProperties.get(NO_PROXY)) + importSettingsState.selectedLODS.set(selectedLODS) + handleCancel() + } + + const handleCancel = () => { + PopoverState.hidePopupover() + } + + return ( + handleSaveChanges()} + onClose={() => handleCancel()} + className="w-[50vw] max-w-2xl" + > +
    +
    +

    Default Import Folder

    + setDefaultImportFolder(val)} /> +
    +
    +

    glTF / glB

    + + {LODGenEnabled && ( +
    +

    LODs Folder

    + setLODImportFolder(val)} /> +

    LODs to Generate

    + {selectedLODS.slice(0, 3).map((LOD, idx) => ( + + ({ + label: sLOD.params.dst, + value: sLOD.params.dst + }))} + value={LOD.params.dst} + onChange={(val) => { + setCurrentLOD(LODList.find((sLOD) => sLOD.params.dst === val) ?? LODList[0]) + setCurrentIndex(idx) + }} + /> + + ))} +
    + )} +
    +
    +

    Images

    +

    Compression Settings

    +
    + + { + setKTXEnabled(val) + }} + /> + + {KTXEnabled && } +
    +
    +
    +
    + ) +} + +export default ImportSettingsPanel + +/* + + Default Import Folder + { + setDefaultImportFolder(event.target.value) + }} + /> + + + glTF / glB + setLODGenEnabled(!LODGenEnabled)} />} + label={'Generate LODs'} + /> + {LODGenEnabled && ( + <> + LODs Folder + { + setLODImportFolder(event.target.value) + }} + /> + LODs to Generate + + {selectedLODS.slice(0, 3).map((LOD, idx) => ( + + + {LODList.map((sLOD) => ( + { + setCurrentLOD(sLOD) + setCurrentIndex(idx) + }} + > + {sLOD.params.dst} + + ))} + + {presetLabels[idx]} + + ))} + + + )} + + + Images + Compression Settings + + setKTXEnabled(!KTXEnabled)} />} + label={'Compress to KTX2'} + /> + {KTXEnabled && } + + + + + + + */ diff --git a/packages/ui/src/components/editor/toolbar/index.stories.tsx b/packages/ui/src/components/editor/toolbar/index.stories.tsx new file mode 100644 index 00000000000..e2a7cfa9c54 --- /dev/null +++ b/packages/ui/src/components/editor/toolbar/index.stories.tsx @@ -0,0 +1,51 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +const argTypes = {} + +export default { + title: 'Editor/Toolbar', + component: Component, + parameters: { + componentSubtitle: 'Toolbar', + jest: 'Toolbar.test.tsx', + design: { + type: 'figma', + url: '' + } + }, + argTypes +} + +export const Default = { + args: { + label: 'Source Path', + containerClassName: 'w-96', + values: ['test name 1', 'test value 2', 'test 3', 'test 4'], + inputLabel: 'Path' + } +} diff --git a/packages/ui/src/components/editor/toolbar/index.tsx b/packages/ui/src/components/editor/toolbar/index.tsx new file mode 100755 index 00000000000..7c7f8089c3a --- /dev/null +++ b/packages/ui/src/components/editor/toolbar/index.tsx @@ -0,0 +1,51 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import MenuIcon from '@mui/icons-material/Menu' +import WindowIcon from '@mui/icons-material/Window' + +import DropDownMenu from '@etherealengine/editor/src/components/dropDownMenu' +import { EditorNavbarProfile } from '@etherealengine/editor/src/components/projects/EditorNavbarProfile' +import PublishLocation from '@etherealengine/editor/src/components/toolbar/tools//PublishLocation' + +type ToolBarProps = { + menu?: any + panels?: any +} + +export const ToolBar = (props: ToolBarProps) => { + return ( +
    + + + + +
    + ) +} + +export default ToolBar diff --git a/packages/ui/src/components/editor/toolbar/mainMenu/index.tsx b/packages/ui/src/components/editor/toolbar/mainMenu/index.tsx new file mode 100644 index 00000000000..c8a82a3ac70 --- /dev/null +++ b/packages/ui/src/components/editor/toolbar/mainMenu/index.tsx @@ -0,0 +1,97 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' + +import MenuItem from '@mui/material/MenuItem' +import Button from '../../../../primitives/tailwind/Button' +import ContextMenu from '../../layout/ContextMenu' + +interface Command { + name: string + action: () => void + hotkey?: string +} + +interface MainMenuProp { + commands: Command[] + icon: any +} + +const MainMenu = ({ commands, icon }: MainMenuProp) => { + const [anchorPosition, setAnchorPosition] = React.useState({ left: 0, top: 0 }) + const [anchorEl, setAnchorEl] = React.useState(null) + const open = Boolean(anchorEl) + + const onOpen = (event: React.MouseEvent) => { + event.preventDefault() + event.stopPropagation() + + setAnchorEl(event.currentTarget) + setAnchorPosition({ + left: 0, + top: event.currentTarget.offsetHeight + 6 + }) + } + + const handleClose = () => { + setAnchorEl(null) + setAnchorPosition({ left: 0, top: 0 }) + } + + const renderMenu = (command: Command) => { + const menuItem = ( + { + command.action() + handleClose() + }} + > + {command.name} + {command.hotkey &&
    {command.hotkey}
    } +
    + ) + return menuItem + } + + return ( + <> + + + {commands.map((command: Command) => renderMenu(command))} + + + ) +} + +export default MainMenu diff --git a/packages/ui/src/components/editor/toolbar/mainMenu/saveAsScene/index.tsx b/packages/ui/src/components/editor/toolbar/mainMenu/saveAsScene/index.tsx new file mode 100644 index 00000000000..ab1eefcd971 --- /dev/null +++ b/packages/ui/src/components/editor/toolbar/mainMenu/saveAsScene/index.tsx @@ -0,0 +1,63 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { useHookstate } from '@etherealengine/hyperflux' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Input from '../../../../../primitives/tailwind/Input' +import Modal from '../../../../../primitives/tailwind/Modal' + +/** + * SaveNewSceneDialog used to show dialog when to save new scene. + */ +export function SaveNewSceneDialog({ + initialName, + onConfirm, + onCancel +}: { + initialName: string + onConfirm: (value: { name: string }) => void + onCancel: () => void +}) { + const name = useHookstate(initialName) + const { t } = useTranslation() + + return ( + onConfirm({ name: name.value })} + onClose={() => { + onCancel() + PopoverState.hidePopupover() + }} + > + name.set(event.target.value)} /> + + ) +} + +export default SaveNewSceneDialog diff --git a/packages/ui/src/components/editor/toolbar/mainMenu/saveScene/index.tsx b/packages/ui/src/components/editor/toolbar/mainMenu/saveScene/index.tsx new file mode 100644 index 00000000000..53cf5ddb2b5 --- /dev/null +++ b/packages/ui/src/components/editor/toolbar/mainMenu/saveScene/index.tsx @@ -0,0 +1,52 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { useTranslation } from 'react-i18next' + +import ConfirmDialog from '../../../../tailwind/ConfirmDialog' + +/** + * SaveSceneDialog used to show dialog when to save scene. + */ +export function SaveSceneDialog({ + onConfirm, + onCancel +}: { + onConfirm: (val: boolean) => void + onCancel: (val?: boolean) => void +}) { + const { t } = useTranslation() + + return ( + onConfirm(true)} + onClose={onCancel} + /> + ) +} + +export default SaveSceneDialog diff --git a/packages/ui/src/components/editor/util/PopoverContext.tsx b/packages/ui/src/components/editor/util/PopoverContext.tsx new file mode 100644 index 00000000000..1a5d1c55fa9 --- /dev/null +++ b/packages/ui/src/components/editor/util/PopoverContext.tsx @@ -0,0 +1,30 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { createContext, useContext } from 'react' + +export const PopoverContext = createContext({ handlePopoverClose: () => {} }) + +export const usePopoverContextClose = () => useContext(PopoverContext).handlePopoverClose diff --git a/packages/ui/src/components/tailwind/ConfirmDialog/index.tsx b/packages/ui/src/components/tailwind/ConfirmDialog/index.tsx index c2857d057eb..1ba5a292a1f 100644 --- a/packages/ui/src/components/tailwind/ConfirmDialog/index.tsx +++ b/packages/ui/src/components/tailwind/ConfirmDialog/index.tsx @@ -33,11 +33,12 @@ import Text from '../../../primitives/tailwind/Text' interface ConfirmDialogProps { text: string - onSubmit: () => Promise | void + onSubmit: () => Promise + onClose?: () => void modalProps?: Partial } -export const ConfirmDialog = ({ text, onSubmit, modalProps }: ConfirmDialogProps) => { +export const ConfirmDialog = ({ text, onSubmit, onClose, modalProps }: ConfirmDialogProps) => { const errorText = useHookstate('') const modalProcessing = useHookstate(false) @@ -56,7 +57,10 @@ export const ConfirmDialog = ({ text, onSubmit, modalProps }: ConfirmDialogProps { + PopoverState.hidePopupover() + onClose?.() + }} className="w-[50vw] max-w-2xl" submitLoading={modalProcessing.value} {...modalProps} diff --git a/packages/ui/src/components/tailwind/ErrorDialog/index.tsx b/packages/ui/src/components/tailwind/ErrorDialog/index.tsx new file mode 100644 index 00000000000..c6c2a9b1454 --- /dev/null +++ b/packages/ui/src/components/tailwind/ErrorDialog/index.tsx @@ -0,0 +1,52 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import { PopoverState } from '@etherealengine/client-core/src/common/services/PopoverState' +import { t } from 'i18next' +import React from 'react' +import ErrorView from '../../../primitives/tailwind/ErrorView' +import Modal, { ModalProps } from '../../../primitives/tailwind/Modal' + +interface ErrorDialogProps { + title: string + description?: string + modalProps?: ModalProps +} + +const ErrorDialog = ({ title, description, modalProps }: ErrorDialogProps) => { + return ( + PopoverState.hidePopupover()} + className="w-[50vw] max-w-2xl" + {...modalProps} + > + + + ) +} + +export default ErrorDialog diff --git a/packages/ui/src/fonts/font.css b/packages/ui/src/fonts/font.css new file mode 100644 index 00000000000..bdc9442616f --- /dev/null +++ b/packages/ui/src/fonts/font.css @@ -0,0 +1 @@ +@import url('https://fonts.googleapis.com/css2?family=Figtree:ital,wght@0,300..900;1,300..900&display=swap') \ No newline at end of file diff --git a/packages/ui/src/primitives/tailwind/Checkbox/index.tsx b/packages/ui/src/primitives/tailwind/Checkbox/index.tsx index f74e0ba9a70..5e03ea1a6d4 100644 --- a/packages/ui/src/primitives/tailwind/Checkbox/index.tsx +++ b/packages/ui/src/primitives/tailwind/Checkbox/index.tsx @@ -28,35 +28,45 @@ import { twMerge } from 'tailwind-merge' import Label from '../Label' -export interface CheckboxProps { +export interface CheckboxProps extends Omit, 'value' | 'onChange'> { value: boolean label?: string className?: string + containerClassName?: string onChange: (value: boolean) => void disabled?: boolean } -const Checkbox = ({ className, label, value, onChange, disabled }: CheckboxProps) => { +const Checkbox = ({ className, containerClassName, label, value, onChange, disabled, ...rest }: CheckboxProps) => { const twClassName = twMerge( - 'h-4 w-4 rounded', - 'border-gray-300 bg-gray-100 text-blue-400 focus:ring-2 focus:ring-blue-500 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-700 dark:ring-offset-gray-800 dark:focus:ring-blue-600', + 'disabled:border-steel-400 disabled:bg-steel-400 peer', + 'relative h-4 w-4 shrink-0 appearance-none rounded-sm', + 'border-2 border-blue-500 bg-white', + 'checked:border-0 checked:bg-blue-800 focus:outline-none focus:ring-2', + 'focus:ring-blue-500 focus:ring-offset-0 dark:focus:ring-blue-600', className ) return ( -
    - onChange(!value)} - disabled={disabled} - /> +
    + onChange(e.target.value as any)} className={twClassName} {...rest} /> {label && ( )} + + +
    ) } diff --git a/packages/ui/src/primitives/tailwind/Color/index.stories.tsx b/packages/ui/src/primitives/tailwind/Color/index.stories.tsx new file mode 100644 index 00000000000..1628ef52f9e --- /dev/null +++ b/packages/ui/src/primitives/tailwind/Color/index.stories.tsx @@ -0,0 +1,42 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ +import Color from './index' + +export default { + title: 'Primitives/Tailwind/Color', + component: Color, + parameters: { + componentSubtitle: 'Color', + jest: 'Color.test.tsx', + design: { + type: 'figma', + url: '' + } + } +} + +export const Default = { + args: {} +} diff --git a/packages/ui/src/primitives/tailwind/Color/index.tsx b/packages/ui/src/primitives/tailwind/Color/index.tsx new file mode 100644 index 00000000000..798ef955c81 --- /dev/null +++ b/packages/ui/src/primitives/tailwind/Color/index.tsx @@ -0,0 +1,93 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import { ColorResult } from '@uiw/color-convert' +import SketchPicker from '@uiw/react-color-sketch' +import React from 'react' +import { Color } from 'three' + +import { twMerge } from 'tailwind-merge' +import Text from '../Text' + +interface ColorInputProp { + value: Color + onChange: (color: Color) => void + disabled?: boolean + isValueAsInteger?: boolean + className?: string + textClassName?: string + sketchPickerClassName?: string +} + +export function ColorInput({ + value, + onChange, + disabled, + className, + textClassName, + sketchPickerClassName +}: ColorInputProp) { + const hexColor = typeof value.getHexString === 'function' ? '#' + value.getHexString() : '#000' + + const handleChange = (result: ColorResult) => { + const color = new Color(result.hex) + onChange(color) + } + + return ( +
    +
    + +
    + + {hexColor.toUpperCase()} + +
    + ) +} + +ColorInput.defaultProps = { + value: new Color(), + onChange: () => {} +} + +export default ColorInput diff --git a/packages/ui/src/primitives/tailwind/Slider/index.stories.tsx b/packages/ui/src/primitives/tailwind/Slider/index.stories.tsx new file mode 100644 index 00000000000..451a128d88b --- /dev/null +++ b/packages/ui/src/primitives/tailwind/Slider/index.stories.tsx @@ -0,0 +1,43 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import Component from './index' + +export default { + title: 'Primitives/Tailwind/Slider', + component: Component, + parameters: { + componentSubtitle: 'Slider', + jest: 'Slider.test.tsx', + design: { + type: 'figma', + url: '' + } + } +} + +export const Default = { + args: {} +} diff --git a/packages/ui/src/primitives/tailwind/Slider/index.tsx b/packages/ui/src/primitives/tailwind/Slider/index.tsx new file mode 100644 index 00000000000..4c3f151c712 --- /dev/null +++ b/packages/ui/src/primitives/tailwind/Slider/index.tsx @@ -0,0 +1,129 @@ +/* +CPAL-1.0 License + +The contents of this file are subject to the Common Public Attribution License +Version 1.0. (the "License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at +https://github.com/EtherealEngine/etherealengine/blob/dev/LICENSE. +The License is based on the Mozilla Public License Version 1.1, but Sections 14 +and 15 have been added to cover use of software over a computer network and +provide for limited attribution for the Original Developer. In addition, +Exhibit A has been modified to be consistent with Exhibit B. + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the +specific language governing rights and limitations under the License. + +The Original Code is Ethereal Engine. + +The Original Developer is the Initial Developer. The Initial Developer of the +Original Code is the Ethereal Engine team. + +All portions of the code written by the Ethereal Engine team are Copyright © 2021-2023 +Ethereal Engine. All Rights Reserved. +*/ + +import React from 'react' +import { twMerge } from 'tailwind-merge' + +export interface SliderProps { + className?: string + value: number + min?: number + max?: number + step?: number + width?: number + onChange: (value: number) => void + onRelease: (value: number) => void +} + +/** + * @param props.width width of the slider in pixels + */ +const Slider = ({ value, min = 0, max = 100, step = 1, width = 200, onChange, onRelease, className }: SliderProps) => { + const handleInputChange = (event: React.ChangeEvent) => { + let newValue = parseFloat(event.target.value) + if (isNaN(newValue)) { + newValue = min + } else { + newValue = Math.min(Math.max(newValue, min), max) + } + onChange(newValue) + } + + const handleChange = (event: React.ChangeEvent) => { + const newValue = parseFloat(event.target.value) + onChange(newValue) + } + + return ( +
    + onRelease(value)} + className="h-8 w-14 rounded bg-neutral-900 text-center font-['Figtree'] text-sm font-normal leading-[21px] text-neutral-400" + /> + onRelease(value)} + step={step} + type="range" + className={twMerge( + `w-[${width}px] h-8 cursor-pointer appearance-none overflow-hidden rounded bg-[#111113] from-[#214AA6] via-[#214AA6] focus:outline-none + + disabled:pointer-events-none + disabled:opacity-50 + [&::-moz-range-progress]:bg-[#214AA6] + [&::-moz-range-track]:h-full + [&::-moz-range-track]:w-full + + [&::-moz-range-track]:rounded + [&::-moz-range-track]:bg-[#111113] + [&::-webkit-slider-runnable-track]:h-full + [&::-webkit-slider-runnable-track]:w-full [&::-webkit-slider-runnable-track]:rounded [&::-webkit-slider-runnable-track]:bg-gradient-to-r via-[${Math.round( + ((value - min) / (max - min)) * 95 + )}%] to-[${Math.round(((value - min) / (max - min)) * 100)}%] + [&::-webkit-slider-thumb]:h-full + [&::-webkit-slider-thumb]:shadow-[-${width}px_0_0_${width}px_#214AA6] + + [&::-moz-range-thumb]:h-full + [&::-moz-range-thumb]:w-4 + [&::-moz-range-thumb]:appearance-none + [&::-moz-range-thumb]:rounded + [&::-moz-range-thumb]:bg-[#849ED6] + + [&::-moz-range-thumb]:transition-all + [&::-moz-range-thumb]:duration-150 + [&::-moz-range-thumb]:ease-in-out + + [&::-webkit-slider-thumb]:w-4 + [&::-webkit-slider-thumb]:appearance-none + [&::-webkit-slider-thumb]:rounded + [&::-webkit-slider-thumb]:bg-[#849ED6] + + [&::-webkit-slider-thumb]:transition-all + [&::-webkit-slider-thumb]:duration-150 + [&::-webkit-slider-thumb]:ease-in-out + `, + className + )} + /> +
    + ) +} + +Slider.defaultProps = { + min: 0, + max: 100, + step: 1, + value: 60 +} + +export default Slider diff --git a/tailwind.config.js b/tailwind.config.js index 3fba8fcaf1e..0845945e4e1 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -31,6 +31,12 @@ module.exports = { important: true, // important in prod is must be theme: { extend: { + gradientColorStops: { + ...Array.from({ length: 101 }, (_, i) => i).reduce((acc, curr) => { + acc[curr] = `${curr}%`; + return acc; + }, {}) + }, backgroundImage: { 'gradient-onboarding': 'linear-gradient(180deg, #0A0A0A 0%, #262626 100%)', 'text-gradient-onboarding': 'linear-gradient(275deg, #4195FB 4.98%, #4E9CFB 61.64%, #A5CDFD 97.96%)', @@ -70,5 +76,11 @@ module.exports = { 'blue-primary': '#375DAF' } } + }, + purge: { + safelist: [ + ...Array.from({ length: 101 }, (_, i) => `via-[${i}%]`), + ...Array.from({ length: 101 }, (_, i) => `to-[${i}%]`) + ] } }