diff --git a/packages/client-core/src/admin/adminRoutes.tsx b/packages/client-core/src/admin/adminRoutes.tsx index 5112926c2f..fb4f1f2e5a 100644 --- a/packages/client-core/src/admin/adminRoutes.tsx +++ b/packages/client-core/src/admin/adminRoutes.tsx @@ -71,7 +71,7 @@ const AdminTopBar = () => { )} - + diff --git a/packages/client-core/src/admin/components/project/ProjectTable.tsx b/packages/client-core/src/admin/components/project/ProjectTable.tsx index 42c99da25b..a664aeff37 100644 --- a/packages/client-core/src/admin/components/project/ProjectTable.tsx +++ b/packages/client-core/src/admin/components/project/ProjectTable.tsx @@ -221,12 +221,12 @@ export default function ProjectTable(props: { search: string }) { {row.name} {!!row.needsRebuild && ( - + )} {!!row.hasLocalChanges && ( - + )} @@ -243,7 +243,7 @@ export default function ProjectTable(props: { search: string }) { visibility: handleVisibilityChange(row)} />, commitSHA: ( - + <>{row.commitSHA?.slice(0, 8)} {' '} diff --git a/packages/client-core/src/admin/components/server/ServerTable.tsx b/packages/client-core/src/admin/components/server/ServerTable.tsx index 7663191f09..a98544ec45 100644 --- a/packages/client-core/src/admin/components/server/ServerTable.tsx +++ b/packages/client-core/src/admin/components/server/ServerTable.tsx @@ -66,7 +66,7 @@ function ServerStatus({ serverPodInfo }: { serverPodInfo: ServerPodInfoType }) { )}
{serverPodInfo.containers.map((container) => ( - +
))} diff --git a/packages/client-core/src/admin/components/settings/tabs/features.tsx b/packages/client-core/src/admin/components/settings/tabs/features.tsx index 7bfe316da7..bdca91c495 100644 --- a/packages/client-core/src/admin/components/settings/tabs/features.tsx +++ b/packages/client-core/src/admin/components/settings/tabs/features.tsx @@ -121,7 +121,7 @@ const FeatureItem = ({ feature }: { feature: FeatureFlagSettingType }) => { /> {feature.userId && ( {appleIp ? ( - + ) : null} {discordIp ? ( - + ) : null} {googleIp ? ( - + ) : null} {facebookIp ? ( - + ) : null} {twitterIp ? ( - + ) : null} {linkedinIp ? ( - + ) : null} {githubIp ? ( - + ) : null} {smsIp ? ( - + ) : null} {emailIp ? ( - + ) : null} diff --git a/packages/common/src/regex/index.ts b/packages/common/src/regex/index.ts index 09b7cbb178..610dd4ebc6 100644 --- a/packages/common/src/regex/index.ts +++ b/packages/common/src/regex/index.ts @@ -26,12 +26,11 @@ Ethereal Engine. All Rights Reserved. // Add unit tests for new patterns in packages/common/tests/regex.test.ts // eslint-disable-next-line no-control-regex -export const INVALID_FILENAME_REGEX = /[_<>:"/\\|?*\u0000-\u001F]/ +export const VALID_FILENAME_REGEX = /^(?!.*[\s_<>:"/\\|?*\u0000-\u001F].*)[^\s_<>:"/\\|?*\u0000-\u001F]{1,64}$/ // eslint-disable-next-line no-control-regex -export const INVALID_FILENAME_WHITESPACE_REGEX = /[\s_<>:"/\\|?*\u0000-\u001F]/ export const WINDOWS_RESERVED_NAME_REGEX = /^(con|prn|aux|nul|com\d|lpt\d)$/i export const VALID_SCENE_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9-]{2,62}[a-zA-Z0-9_\-]$/ -export const VALID_HEIRACHY_SEARCH_REGEX = /[.*+?^${}()|[\]\\]/g +export const VALID_HEIRARCHY_SEARCH_REGEX = /[.*+?^${}()|[\]\\]/g /** * Matches CSS imports & URLS. diff --git a/packages/ui/src/components/editor/layout/Tooltip.tsx b/packages/common/src/utils/offsets.ts old mode 100755 new mode 100644 similarity index 65% rename from packages/ui/src/components/editor/layout/Tooltip.tsx rename to packages/common/src/utils/offsets.ts index bd511855c8..984f49135d --- a/packages/ui/src/components/editor/layout/Tooltip.tsx +++ b/packages/common/src/utils/offsets.ts @@ -23,19 +23,22 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import React from 'react' -import Tooltip, { TooltipProps } from '../../../primitives/tailwind/Tooltip' - -export function InfoTooltip({ title, info, ...props }: TooltipProps & { info?: string }) { - const tooltipTitle = info ? ( -

- {title} -


- {info} -

- ) : ( - title - ) - - return +import { Bounds, getBounds, getViewportBounds } from '@etherealengine/xrui' + +export const calculateAndApplyYOffset = (element: HTMLElement | null, additionalOffset = 0) => { + if (!element) { + return + } + const popupBounds = getBounds(element) + const viewportBounds = getViewportBounds(new Bounds()) + + const overflowBottom = + (popupBounds?.top ?? 0) + (popupBounds?.height ?? 0) - (viewportBounds.top + viewportBounds.height) + let offsetY = 0 + + if (overflowBottom > 0) { + offsetY = -(popupBounds?.height ?? 0) + additionalOffset + } + + element.style.transform = `translateY(${offsetY}px)` } diff --git a/packages/common/src/utils/validateFileName.ts b/packages/common/src/utils/validateFileName.ts index 0d38077fd9..cf4ad4f553 100644 --- a/packages/common/src/utils/validateFileName.ts +++ b/packages/common/src/utils/validateFileName.ts @@ -23,12 +23,8 @@ All portions of the code written by the Ethereal Engine team are Copyright © 20 Ethereal Engine. All Rights Reserved. */ -import { INVALID_FILENAME_WHITESPACE_REGEX, WINDOWS_RESERVED_NAME_REGEX } from '@etherealengine/common/src/regex' +import { VALID_FILENAME_REGEX, WINDOWS_RESERVED_NAME_REGEX } from '@etherealengine/common/src/regex' export function isValidFileName(fileName: string) { - return ( - !INVALID_FILENAME_WHITESPACE_REGEX.test(fileName) && - !WINDOWS_RESERVED_NAME_REGEX.test(fileName) && - fileName.length > 0 - ) + return VALID_FILENAME_REGEX.test(fileName) && !WINDOWS_RESERVED_NAME_REGEX.test(fileName) } diff --git a/packages/common/tests/regex.test.ts b/packages/common/tests/regex.test.ts index 40f29c42bf..6f3c7b4a4b 100644 --- a/packages/common/tests/regex.test.ts +++ b/packages/common/tests/regex.test.ts @@ -31,8 +31,6 @@ import { EMAIL_REGEX, GITHUB_URL_REGEX, INSTALLATION_SIGNED_REGEX, - INVALID_FILENAME_REGEX, - INVALID_FILENAME_WHITESPACE_REGEX, INVITE_CODE_REGEX, MAIN_CHART_REGEX, PHONE_REGEX, @@ -42,7 +40,8 @@ import { PUBLIC_SIGNED_REGEX, STATIC_ASSET_REGEX, USER_ID_REGEX, - VALID_HEIRACHY_SEARCH_REGEX, + VALID_FILENAME_REGEX, + VALID_HEIRARCHY_SEARCH_REGEX, VALID_PROJECT_NAME, VALID_SCENE_NAME_REGEX, WINDOWS_RESERVED_NAME_REGEX @@ -50,7 +49,7 @@ import { describe('regex.test', () => { describe('INVALID_FILENAME_REGEX', () => { - it('should match invalid filenames', () => { + it('should not match invalid filenames', () => { const invalidFilenames = [ 'hello_world', 'file { 'question?mark', 'asterisk*char', 'control\0char', - 'another\ncontrol' + 'another\ncontrol', + 'file name', + '< tag >', + 'key : value', + 'quote " example', + 'path / to / file', + 'C:\\ path \\ to \\ file', + 'pipe | character', + 'question ? mark', + 'star * char' ] invalidFilenames.forEach((filename) => { - assert.ok(INVALID_FILENAME_REGEX.test(filename), `Expected '${filename}' to be invalid`) + assert.ok(!VALID_FILENAME_REGEX.test(filename), `Expected '${filename}' to be invalid`) }) }) - it('should not match valid filenames', () => { + it('should match valid filenames', () => { const validFilenames = [ 'helloworld', 'filename', @@ -82,7 +90,7 @@ describe('regex.test', () => { 'anothercontrol' ] validFilenames.forEach((filename) => { - assert.ok(!INVALID_FILENAME_REGEX.test(filename), `Expected '${filename}' to be valid`) + assert.ok(VALID_FILENAME_REGEX.test(filename), `Expected '${filename}' to be valid`) }) }) }) @@ -90,7 +98,7 @@ describe('regex.test', () => { describe('HEIRARCHY_SEARCH_REPLACE_REGEX', () => { it('should replace special characters in search', () => { const escapeSpecialChars = (input) => { - return input.replace(VALID_HEIRACHY_SEARCH_REGEX, '\\$&') + return input.replace(VALID_HEIRARCHY_SEARCH_REGEX, '\\$&') } const testCases = [ @@ -117,43 +125,6 @@ describe('regex.test', () => { }) }) - describe('INVALID_FILENAME_WHITESPACE_REGEX', () => { - it('should match invalid filenames', () => { - const invalidFilenames = [ - 'file name', // (a space and an underscore) - '< tag >', // (spaces and less-than < and greater-than > characters) - 'key : value', // (a space and a colon :) - 'quote " example', // (a space and a double quote ") - 'path / to / file', // (spaces and forward slashes /) - 'C:\\ path \\ to \\ file', // (spaces and backslashes \) - 'pipe | character', // (a space and a pipe |) - 'question ? mark', // (a space and a question mark ?) - 'star * char' // (a space and an asterisk *) - ] - invalidFilenames.forEach((filename) => { - assert.ok(INVALID_FILENAME_WHITESPACE_REGEX.test(filename), `Expected '${filename}' to be invalid`) - }) - }) - - it('should not match valid filenames', () => { - const validFilenames = [ - 'helloworld', - 'filename', - 'emailexample.com', - 'pathtofile', - 'backslash', - 'pipesymbol', - 'questionmark', - 'asteriskchar', - 'controlchar', - 'anothercontrol' - ] - validFilenames.forEach((filename) => { - assert.ok(!INVALID_FILENAME_WHITESPACE_REGEX.test(filename), `Expected '${filename}' to be valid`) - }) - }) - }) - describe('WINDOWS_RESERVED_NAME_REGEX', () => { it('should match windows reserved names', () => { const reservedNames = ['CON', 'PrN', 'auX', 'NUL', 'COM0', 'com1', 'Com9', 'LPT0', 'LpT4', 'lPt9'] diff --git a/packages/editor/src/components/dialogs/ImportSettingsPanelDialog.tsx b/packages/editor/src/components/dialogs/ImportSettingsPanelDialog.tsx index 3af31d0272..77523e0ec9 100644 --- a/packages/editor/src/components/dialogs/ImportSettingsPanelDialog.tsx +++ b/packages/editor/src/components/dialogs/ImportSettingsPanelDialog.tsx @@ -78,7 +78,7 @@ const ImageCompressionBox = ({ compressProperties }: { compressProperties: State onChange={compressProperties.flipY.set} label={t('editor:properties.model.transform.flipY')} /> - +
@@ -88,7 +88,7 @@ const ImageCompressionBox = ({ compressProperties }: { compressProperties: State onChange={compressProperties.srgb.set} label={t('editor:properties.model.transform.srgb')} /> - +
@@ -98,7 +98,7 @@ const ImageCompressionBox = ({ compressProperties }: { compressProperties: State onChange={compressProperties.mipmaps.set} label={t('editor:properties.model.transform.mipmaps')} /> - + @@ -108,7 +108,7 @@ const ImageCompressionBox = ({ compressProperties }: { compressProperties: State onChange={compressProperties.normalMap.set} label={t('editor:properties.model.transform.normalMap')} /> - + diff --git a/packages/engine/src/assets/loaders/gltf/extensions/CachedImageLoadExtension.ts b/packages/engine/src/assets/loaders/gltf/extensions/CachedImageLoadExtension.ts index 706cbe7c98..683ed2f24a 100644 --- a/packages/engine/src/assets/loaders/gltf/extensions/CachedImageLoadExtension.ts +++ b/packages/engine/src/assets/loaders/gltf/extensions/CachedImageLoadExtension.ts @@ -35,7 +35,9 @@ class CachedImageLoadExtension extends ImporterExtension implements GLTFLoaderPl loadTexture(textureIndex) { const options = this.parser.options - if (!options.url?.endsWith('.gltf')) { + const baseURL = new URL(options.url) + + if (!baseURL.pathname.endsWith('.gltf')) { return this.parser.loadTexture(textureIndex) } const json = this.parser.json diff --git a/packages/engine/src/gltf/GLTFComponent.tsx b/packages/engine/src/gltf/GLTFComponent.tsx index 75600d17a1..c2803003e4 100644 --- a/packages/engine/src/gltf/GLTFComponent.tsx +++ b/packages/engine/src/gltf/GLTFComponent.tsx @@ -30,18 +30,21 @@ import { parseStorageProviderURLs } from '@etherealengine/common/src/utils/parse import { defineComponent, Entity, + EntityUUID, getComponent, getMutableComponent, getOptionalComponent, hasComponent, useComponent, useEntityContext, - useQuery + useQuery, + UUIDComponent } from '@etherealengine/ecs' import { dispatchAction, getState, useHookstate } from '@etherealengine/hyperflux' import { FileLoader } from '../assets/loaders/base/FileLoader' import { BINARY_EXTENSION_HEADER_MAGIC, EXTENSIONS, GLTFBinaryExtension } from '../assets/loaders/gltf/GLTFExtensions' +import { ModelComponent } from '../scene/components/ModelComponent' import { SourceComponent } from '../scene/components/SourceComponent' import { SceneJsonType } from '../scene/types/SceneTypes' import { migrateSceneJSONToGLTF } from './convertJsonToGLTF' @@ -83,7 +86,19 @@ const ResourceReactor = (props: { documentID: string; entity: Entity }) => { useEffect(() => { if (getComponent(props.entity, GLTFComponent).progress === 100) return if (!getState(GLTFDocumentState)[props.documentID]) return - + const document = getState(GLTFDocumentState)[props.documentID] + const modelNodes = document.nodes?.filter((node) => !!node.extensions?.[ModelComponent.jsonID]) + if (modelNodes) { + for (const node of modelNodes) { + //check if an entity exists for this node, and has a model component + const uuid = node.extensions![UUIDComponent.jsonID] as EntityUUID + if (!UUIDComponent.entitiesByUUIDState[uuid]) return + const entity = UUIDComponent.entitiesByUUIDState[uuid].value + const model = getOptionalComponent(entity, ModelComponent) + //ensure that model contents have been loaded into the scene + if (!model?.scene) return + } + } const entities = resourceQuery.filter((e) => getComponent(e, SourceComponent) === props.documentID) if (!entities.length) { getMutableComponent(props.entity, GLTFComponent).progress.set(100) diff --git a/packages/ui/src/components/editor/input/Group/index.tsx b/packages/ui/src/components/editor/input/Group/index.tsx index a3dabb78df..33814b4b7d 100644 --- a/packages/ui/src/components/editor/input/Group/index.tsx +++ b/packages/ui/src/components/editor/input/Group/index.tsx @@ -30,7 +30,6 @@ import { MdOutlineHelpOutline } from 'react-icons/md' 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. @@ -98,21 +97,6 @@ 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 @@ -142,7 +126,7 @@ export function InputGroup({
{info && ( - + )} diff --git a/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx b/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx index e9cb56064d..1aae8099e0 100644 --- a/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx +++ b/packages/ui/src/components/editor/input/Numeric/Stepper/index.tsx @@ -29,7 +29,7 @@ import { FaChevronLeft, FaChevronRight } from 'react-icons/fa' import { t } from 'i18next' import { twMerge } from 'tailwind-merge' import NumericInput, { NumericInputProp } from '..' -import { InfoTooltip } from '../../../layout/Tooltip' +import Tooltip from '../../../../../primitives/tailwind/Tooltip' export function NumericStepperInput({ style, @@ -54,23 +54,23 @@ export function NumericStepperInput({ return (
- + - + - + - +
) } diff --git a/packages/ui/src/components/editor/panels/Assets/container/index.tsx b/packages/ui/src/components/editor/panels/Assets/container/index.tsx index 8d7f69d3ce..1deb83edf9 100644 --- a/packages/ui/src/components/editor/panels/Assets/container/index.tsx +++ b/packages/ui/src/components/editor/panels/Assets/container/index.tsx @@ -23,8 +23,8 @@ 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 { clone, debounce, isEmpty } from 'lodash' -import React, { useEffect, useRef } from 'react' +import { clone, debounce } from 'lodash' +import React, { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { NotificationService } from '@etherealengine/client-core/src/common/services/NotificationService' @@ -55,15 +55,17 @@ import { twMerge } from 'tailwind-merge' import Button from '../../../../../primitives/tailwind/Button' import Input from '../../../../../primitives/tailwind/Input' import LoadingView from '../../../../../primitives/tailwind/LoadingView' -import { TablePagination } from '../../../../../primitives/tailwind/Table' import Tooltip from '../../../../../primitives/tailwind/Tooltip' import { ContextMenu } from '../../../../tailwind/ContextMenu' +import InfiniteScroll from '../../../../tailwind/InfiniteScroll' import DeleteFileModal from '../../Files/browserGrid/DeleteFileModal' import { ViewModeSettings } from '../../Files/container' import { FileIcon } from '../../Files/icon' import { FileUploadProgress } from '../../Files/upload/FileUploadProgress' import { AssetIconMap } from '../icons' +const ASSETS_PAGE_LIMIT = 10 + type Category = { name: string object: object @@ -183,7 +185,7 @@ const ResourceFile = (props: { - + {name} @@ -237,10 +239,6 @@ const ResourceFile = (props: { ) } -export const MenuDivider = () => { - return
-} - interface MetadataTableProps { rows: MetadataTableRowProps[] } @@ -308,39 +306,49 @@ const AssetCategory = (props: { // TODO: add preview functionality } - const iconSize = useHookstate(getMutableState(FilesViewModeSettings).list.fontSize).value + const fontSize = useHookstate(getMutableState(FilesViewModeSettings).list.fontSize).value return (
1 ? category.depth * 16 : 0, - height: iconSize, - fontSize: iconSize + height: `${fontSize}px`, + fontSize: `${fontSize}px` }} - onClick={handleSelectCategory} > -
) @@ -405,7 +413,7 @@ const CategoriesList = ({ return (
@@ -438,11 +446,13 @@ const AssetPanel = () => { const searchedStaticResources = useHookstate([]) const searchText = useHookstate('') const originalPath = useMutableState(EditorState).projectName.value - const staticResourcesPagination = useHookstate({ totalPages: -1, currentPage: 0 }) + const staticResourcesPagination = useHookstate({ total: 0, skip: 0 }) const assetsPreviewContext = useHookstate({ selectAssetURL: '' }) const parentCategories = useHookstate([]) - const mapCategories = () => categories.set(mapCategoriesHelper(collapsedCategories.value)) + const mapCategories = useCallback(() => { + categories.set(mapCategoriesHelper(collapsedCategories.value)) + }, [categories, collapsedCategories]) useEffect(mapCategories, [collapsedCategories]) useEffect(() => { @@ -452,8 +462,8 @@ const AssetPanel = () => { }, [categories, selectedCategory]) const staticResourcesFindApi = () => { - loading.set(true) searchTimeoutCancelRef.current?.() + loading.set(true) const debouncedSearchQuery = debounce(() => { const tags = selectedCategory.value @@ -484,17 +494,20 @@ const AssetPanel = () => { } : undefined, $sort: { mimeType: 1 }, - $skip: staticResourcesPagination.currentPage.value * 10 + $limit: ASSETS_PAGE_LIMIT, + $skip: Math.min(staticResourcesPagination.skip.value, staticResourcesPagination.total.value) } as StaticResourceQuery Engine.instance.api .service(staticResourcePath) .find({ query }) .then((resources) => { - searchedStaticResources.set(resources.data) - staticResourcesPagination.merge({ totalPages: Math.ceil(resources.total / 10) }) - }) - .then(() => { + if (staticResourcesPagination.skip.value > 0) { + searchedStaticResources.merge(resources.data) + } else { + searchedStaticResources.set(resources.data) + } + staticResourcesPagination.merge({ total: resources.total }) loading.set(false) }) }, 500) @@ -503,49 +516,34 @@ const AssetPanel = () => { searchTimeoutCancelRef.current = debouncedSearchQuery.cancel } - //reset pagination when search text changes - useEffect(() => { - staticResourcesPagination.currentPage.set(0) - }, [searchText]) - - useEffect(() => { - staticResourcesFindApi() - }, [searchText, selectedCategory, staticResourcesPagination.currentPage]) + useEffect(() => staticResourcesPagination.skip.set(0), [searchText]) + useEffect(() => staticResourcesFindApi(), [searchText, selectedCategory, staticResourcesPagination.skip]) - const ResourceItems = () => { - if (loading.value) { - return ( -
- + const ResourceItems = () => ( + <> + {searchedStaticResources.length === 0 && ( +
+ {t('editor:layout.scene-assets.no-search-results')}
- ) - } - return ( - <> - {isEmpty(searchedStaticResources.value) && ( -
- {t('editor:layout.scene-assets.no-search-results')} -
- )} - {!isEmpty(searchedStaticResources.value) && ( - <> - {searchedStaticResources.value.map((resource) => ( - { - assetsPreviewContext.selectAssetURL.set(props.resourceUrl) - ClickPlacementState.setSelectedAsset(props.resourceUrl) - }} - onChange={() => staticResourcesFindApi()} - /> - ))} - - )} - - ) - } + )} + {searchedStaticResources.length > 0 && ( + <> + {searchedStaticResources.value.map((resource) => ( + { + assetsPreviewContext.selectAssetURL.set(props.resourceUrl) + ClickPlacementState.setSelectedAsset(props.resourceUrl) + }} + onChange={() => staticResourcesFindApi()} + /> + ))} + + )} + + ) const handleBack = () => { if (!parentCategories.length) { @@ -565,7 +563,7 @@ const AssetPanel = () => { const handleSelectCategory = (category: Category) => { selectedCategory.set(clone(category)) - staticResourcesPagination.currentPage.set(0) + staticResourcesPagination.skip.set(0) !category.isLeaf && collapsedCategories[category.name].set(!category.collapsed) } @@ -592,13 +590,13 @@ const AssetPanel = () => {
- +
- +
@@ -667,18 +665,16 @@ const AssetPanel = () => {
-
+ = staticResourcesPagination.total.value} + onScrollBottom={() => staticResourcesPagination.skip.set((prevSkip) => prevSkip + ASSETS_PAGE_LIMIT)} + >
-
-
- staticResourcesPagination.merge({ currentPage: newPage })} - /> -
+ {loading.value && } + +
{/*
TODO: add preview functionality
*/}
diff --git a/packages/ui/src/components/editor/panels/Files/browserGrid/RenameFileModal.tsx b/packages/ui/src/components/editor/panels/Files/browserGrid/RenameFileModal.tsx index b7ded4a299..0f7f543e65 100644 --- a/packages/ui/src/components/editor/panels/Files/browserGrid/RenameFileModal.tsx +++ b/packages/ui/src/components/editor/panels/Files/browserGrid/RenameFileModal.tsx @@ -69,6 +69,7 @@ export default function RenameFileModal({ projectName, file }: { projectName: st value={newFileName.value} onChange={(event) => newFileName.set(event.target.value)} errorBorder={!isValid} + description={t('editor:dialog.saveNewScene.info-name')} error={!isValid ? t('editor:layout.filebrowser.renameFileError') : undefined} /> diff --git a/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx b/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx index 50da0ecdf8..70c60946f2 100644 --- a/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx +++ b/packages/ui/src/components/editor/panels/Files/browserGrid/index.tsx @@ -187,7 +187,7 @@ export const FileGridItem: React.FC = (props) => { />
- +
{props.item.fullName}
diff --git a/packages/ui/src/components/editor/panels/Files/container/index.tsx b/packages/ui/src/components/editor/panels/Files/container/index.tsx index 441eab9ae0..3008bb79bc 100644 --- a/packages/ui/src/components/editor/panels/Files/container/index.tsx +++ b/packages/ui/src/components/editor/panels/Files/container/index.tsx @@ -658,27 +658,84 @@ const FileBrowserContentPanel: React.FC = (props) ) } + const ViewModeSettings = () => { + const viewModeSettings = useHookstate(getMutableState(FilesViewModeSettings)) + return ( + +
)}
- +
-
+
{viewModes.map(({ mode, icon }) => (
- +
- +
@@ -803,7 +860,7 @@ export const ViewModeSettings = () => { contentStyle={{ background: '#15171b', border: 'solid', borderColor: '#5d646c' }} position={'bottom left'} trigger={ - + } > -
+
setIsAddEntityMenuOpen(false)} />
diff --git a/packages/ui/src/components/editor/panels/Properties/container/index.tsx b/packages/ui/src/components/editor/panels/Properties/container/index.tsx index e6184a67e3..124a8a207c 100644 --- a/packages/ui/src/components/editor/panels/Properties/container/index.tsx +++ b/packages/ui/src/components/editor/panels/Properties/container/index.tsx @@ -30,13 +30,13 @@ import { UUIDComponent } from '@etherealengine/ecs' import { Component, ComponentJSONIDMap, useOptionalComponent } from '@etherealengine/ecs/src/ComponentFunctions' import { NO_PROXY, getMutableState, getState, useHookstate } from '@etherealengine/hyperflux' +import { calculateAndApplyYOffset } from '@etherealengine/common/src/utils/offsets' 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 { Bounds, getBounds, getViewportBounds } from '@etherealengine/xrui/core/dom-utils' import { HiOutlinePlusCircle } from 'react-icons/hi' import Button from '../../../../../primitives/tailwind/Button' import { Popup } from '../../../../tailwind/Popup' @@ -55,29 +55,6 @@ const EntityComponentEditor = (props: { entity; component; multiEdit }) => { return } -const calculateAndApplyOffset = (popupRef: React.RefObject) => { - if (popupRef.current) { - const popupBounds = getBounds(popupRef.current) - const viewportBounds = getViewportBounds(new Bounds()) - - const overflowTop = viewportBounds.top - (popupBounds?.top ?? 0) - const overflowBottom = - (popupBounds?.top ?? 0) + (popupBounds?.height ?? 0) - (viewportBounds.top + viewportBounds.height) - - let offsetY = 0 - - if (overflowTop > 0) { - // popup is overflowing at the top, move it down - offsetY = overflowTop - } else if (overflowBottom > 0) { - // popup is overflowing at the bottom, move it up - offsetY = -overflowBottom - } - - popupRef.current.style.transform = `translateY(${offsetY}px)` - } -} - const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => { const { t } = useTranslation() const { entityUUID, multiEdit } = props @@ -96,15 +73,14 @@ const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => useEffect(() => { const handleResize = () => { - calculateAndApplyOffset(popupRef) + calculateAndApplyYOffset(popupRef.current) } window.addEventListener('resize', handleResize) - return () => { window.removeEventListener('resize', handleResize) } - }, [popupRef]) + }, []) const [isAddComponentMenuOpen, setIsAddComponentMenuOpen] = useState(false) @@ -128,7 +104,7 @@ const EntityEditor = (props: { entityUUID: EntityUUID; multiEdit: boolean }) => {t('editor:properties.lbl-addComponent')} } - onOpen={() => calculateAndApplyOffset(popupRef)} + onOpen={() => calculateAndApplyYOffset(popupRef.current)} >
setIsAddComponentMenuOpen(false)} /> diff --git a/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx b/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx index c8adf74ff7..b8260493b6 100644 --- a/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx +++ b/packages/ui/src/components/editor/panels/Properties/elementList/index.tsx @@ -130,13 +130,15 @@ const SceneElementListItem = ({ return ( ) } diff --git a/packages/ui/src/components/editor/panels/Properties/index.tsx b/packages/ui/src/components/editor/panels/Properties/index.tsx index f669c9af61..b55725a5bb 100644 --- a/packages/ui/src/components/editor/panels/Properties/index.tsx +++ b/packages/ui/src/components/editor/panels/Properties/index.tsx @@ -26,9 +26,8 @@ Ethereal Engine. All Rights Reserved. import { TabData } from 'rc-dock' import React from 'react' import { useTranslation } from 'react-i18next' +import Tooltip from '../../../../primitives/tailwind/Tooltip' import { PanelDragContainer, PanelTitle } from '../../layout/Panel' -import { InfoTooltip } from '../../layout/Tooltip' -//import styles from '../styles.module.scss' import PropertiesPanelContainer from './container' export const PropertiesPanelTitle = () => { @@ -38,9 +37,7 @@ export const PropertiesPanelTitle = () => {
- - {t('editor:properties.title')} - + {t('editor:properties.title')}
diff --git a/packages/ui/src/components/editor/panels/Properties/material/index.tsx b/packages/ui/src/components/editor/panels/Properties/material/index.tsx index 366aad61de..d9900fab51 100644 --- a/packages/ui/src/components/editor/panels/Properties/material/index.tsx +++ b/packages/ui/src/components/editor/panels/Properties/material/index.tsx @@ -56,11 +56,11 @@ import { } from '@etherealengine/spatial/src/renderer/materials/MaterialComponent' import { formatMaterialArgs, getMaterial } from '@etherealengine/spatial/src/renderer/materials/materialFunctions' import Button from '../../../../../primitives/tailwind/Button' +import Tooltip from '../../../../../primitives/tailwind/Tooltip' import InputGroup from '../../../input/Group' import SelectInput from '../../../input/Select' import StringInput from '../../../input/String' import { PanelDragContainer, PanelTitle } from '../../../layout/Panel' -import { InfoTooltip } from '../../../layout/Tooltip' import ParameterInput from '../../../properties/parameter' type ThumbnailData = { @@ -312,9 +312,9 @@ export const MaterialPropertyTitle = () => {
- - {t('editor:properties.mesh.materialProperties.title')} - + + {t('editor:properties.mesh.materialProperties.title')} +
diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/GizmoTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/GizmoTool.tsx index cd96f35ed9..1b555cc0d8 100644 --- a/packages/ui/src/components/editor/panels/Viewport/tools/GizmoTool.tsx +++ b/packages/ui/src/components/editor/panels/Viewport/tools/GizmoTool.tsx @@ -109,7 +109,7 @@ export default function GizmoTool({
- +
) } diff --git a/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx b/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx index 41ec2263b6..c5f2b35d68 100644 --- a/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx +++ b/packages/ui/src/components/editor/panels/Viewport/tools/TransformPivotTool.tsx @@ -67,7 +67,7 @@ const TransformPivotTool = () => { return (
- +