Compare commits
3 Commits
097912ec57
...
a6a15b0771
Author | SHA1 | Date |
---|---|---|
Bill | a6a15b0771 | |
Bill | 5469c9df36 | |
Laynester | eb605e8c29 |
|
@ -34,6 +34,9 @@
|
|||
"css": "css"
|
||||
},
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["classes \\=([^;]*);", "'([^']*)'"],
|
||||
["classes \\=([^;]*);", "\"([^\"]*)\""],
|
||||
["classes \\=([^;]*);", "\\`([^\\`]*)\\`"]
|
||||
],
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
const lightenHexColor = (hex, percent) =>
|
||||
{
|
||||
// Remove the hash symbol if present
|
||||
hex = hex.replace(/^#/, '');
|
||||
|
||||
// Convert hex to RGB
|
||||
let r = parseInt(hex.substring(0, 2), 16);
|
||||
let g = parseInt(hex.substring(2, 4), 16);
|
||||
let b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
// Adjust RGB values
|
||||
r = Math.round(Math.min(255, r + 255 * percent));
|
||||
g = Math.round(Math.min(255, g + 255 * percent));
|
||||
b = Math.round(Math.min(255, b + 255 * percent));
|
||||
|
||||
// Convert RGB back to hex
|
||||
const result = ((r << 16) | (g << 8) | b).toString(16);
|
||||
|
||||
// Make sure result has 6 digits
|
||||
return '#' + result.padStart(6, '0');
|
||||
}
|
||||
|
||||
const generateShades = (colors) =>
|
||||
{
|
||||
for (let color in colors)
|
||||
{
|
||||
let hex = colors[color]
|
||||
let extended = {}
|
||||
const shades = [ 50, 100, 200, 300, 400, 500, 600, 700, 900, 950 ];
|
||||
|
||||
for (let i = 0; i < shades.length; i++)
|
||||
{
|
||||
let shade = shades[i];
|
||||
extended[shade] = lightenHexColor(hex, shades[(shades.length - 1 - i) ] / 1000);
|
||||
}
|
||||
|
||||
colors[color] = {
|
||||
DEFAULT: hex,
|
||||
...extended
|
||||
}
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateShades,
|
||||
lightenHexColor
|
||||
}
|
|
@ -44,6 +44,7 @@
|
|||
"sass": "^1.72.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^5.1.6"
|
||||
"vite": "^5.1.6",
|
||||
"vite-tsconfig-paths": "^4.3.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { MouseEventType, TouchEventType } from '@nitrots/nitro-renderer';
|
||||
import { CSSProperties, FC, Key, MouseEvent as ReactMouseEvent, ReactNode, TouchEvent as ReactTouchEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { Base } from '..';
|
||||
import { GetLocalStorage, SetLocalStorage, WindowSaveOptions } from '../../api';
|
||||
import { DraggableWindowPosition } from './DraggableWindowPosition';
|
||||
|
||||
|
@ -263,8 +262,8 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
|
|||
|
||||
return (
|
||||
createPortal(
|
||||
<Base className="draggable-window" innerRef={ elementRef } position="absolute" style={ dragStyle } onMouseDownCapture={ onMouseDown } onTouchStartCapture={ onTouchStart }>
|
||||
<div ref={ elementRef } className="absolute draggable-window" style={ dragStyle } onMouseDownCapture={ onMouseDown } onTouchStartCapture={ onTouchStart }>
|
||||
{ children }
|
||||
</Base>, document.getElementById('draggable-windows-container'))
|
||||
</div>, document.getElementById('draggable-windows-container'))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { NitroCard, NitroCardTabs } from '@layout/NitroCard';
|
||||
import { AddLinkEventTracker, ConvertGlobalRoomIdMessageComposer, HabboWebTools, ILinkEventTracker, LegacyExternalInterface, NavigatorInitComposer, NavigatorSearchComposer, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { FaPlus } from 'react-icons/fa';
|
||||
import { LocalizeText, SendMessageComposer, TryVisitRoom } from '../../api';
|
||||
import { Column, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
|
||||
import { Column, NitroCardTabsItemView } from '../../common';
|
||||
import { useNavigator, useNitroEvent } from '../../hooks';
|
||||
import { NavigatorDoorStateView } from './views/NavigatorDoorStateView';
|
||||
import { NavigatorRoomCreatorView } from './views/NavigatorRoomCreatorView';
|
||||
|
@ -196,24 +197,28 @@ export const NavigatorView: FC<{}> = props =>
|
|||
return (
|
||||
<>
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-navigator" uniqueKey="navigator">
|
||||
<NitroCardHeaderView headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
<NitroCard.Main
|
||||
className="w-navigator h-navigator"
|
||||
uniqueKey="navigator">
|
||||
<NitroCard.Header
|
||||
headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') }
|
||||
onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabs.Main>
|
||||
{ topLevelContexts && (topLevelContexts.length > 0) && topLevelContexts.map((context, index) =>
|
||||
{
|
||||
return (
|
||||
<NitroCardTabsItemView key={ index } isActive={ ((topLevelContext === context) && !isCreatorOpen) } onClick={ event => sendSearch('', context.code) }>
|
||||
<NitroCardTabs.Item key={ index } isActive={ ((topLevelContext === context) && !isCreatorOpen) } onClick={ event => sendSearch('', context.code) }>
|
||||
{ LocalizeText(('navigator.toplevelview.' + context.code)) }
|
||||
</NitroCardTabsItemView>
|
||||
</NitroCardTabs.Item>
|
||||
);
|
||||
}) }
|
||||
<NitroCardTabsItemView isActive={ isCreatorOpen } onClick={ event => setCreatorOpen(true) }>
|
||||
<FaPlus className="fa-icon" />
|
||||
</NitroCardTabsItemView>
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView position="relative">
|
||||
</NitroCardTabs.Main>
|
||||
<NitroCard.Content>
|
||||
{ isLoading &&
|
||||
<div className="position-absolute size-full top-0 start-0 z-index-1 bg-muted opacity-0-5" /> }
|
||||
<div className="top-0 position-absolute size-full start-0 z-index-1 bg-muted opacity-0-5" /> }
|
||||
{ !isCreatorOpen &&
|
||||
<>
|
||||
<NavigatorSearchView sendSearch={ sendSearch } />
|
||||
|
@ -222,8 +227,8 @@ export const NavigatorView: FC<{}> = props =>
|
|||
</Column>
|
||||
</> }
|
||||
{ isCreatorOpen && <NavigatorRoomCreatorView /> }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
</NitroCard.Content>
|
||||
</NitroCard.Main> }
|
||||
<NavigatorDoorStateView />
|
||||
{ isRoomInfoOpen && <NavigatorRoomInfoView onCloseClick={ () => setRoomInfoOpen(false) } /> }
|
||||
{ isRoomLinkOpen && <NavigatorRoomLinkView onCloseClick={ () => setRoomLinkOpen(false) } /> }
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
import { DetailedHTMLProps, forwardRef, HTMLAttributes, MouseEvent, PropsWithChildren } from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { classNames, DraggableWindow, DraggableWindowPosition, DraggableWindowProps } from '../common';
|
||||
import { NitroItemCountBadge } from './NitroItemCountBadge';
|
||||
|
||||
const classes = {
|
||||
base: 'flex flex-col rounded shadow',
|
||||
themes: {
|
||||
'primary': 'border'
|
||||
}
|
||||
}
|
||||
|
||||
const NitroCardMain = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
theme?: 'primary';
|
||||
} & DraggableWindowProps> & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { theme = 'primary', uniqueKey = null, handleSelector = '.drag-handler', windowPosition = DraggableWindowPosition.CENTER, disableDrag = false, className = null, ...rest } = props;
|
||||
|
||||
return (
|
||||
<DraggableWindow disableDrag={ disableDrag } handleSelector={ handleSelector } uniqueKey={ uniqueKey } windowPosition={ windowPosition }>
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
classes.base,
|
||||
classes.themes[theme],
|
||||
className
|
||||
) }
|
||||
{ ...rest } />
|
||||
</DraggableWindow>
|
||||
);
|
||||
});
|
||||
|
||||
NitroCardMain.displayName = 'NitroCardMain';
|
||||
|
||||
const NitroCardHeader = forwardRef<HTMLDivElement, {
|
||||
headerText: string;
|
||||
onCloseClick?: (event: MouseEvent) => void;
|
||||
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { headerText = '', onCloseClick = null, className = null, ...rest } = props;
|
||||
|
||||
const onMouseDown = (event: MouseEvent<HTMLDivElement>) =>
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={ ref } className={ classNames('relative flex items-center justify-center flex-column drag-handler', className) }>
|
||||
<div className="flex items-center justify-center w-full">
|
||||
<span>{ headerText }</span>
|
||||
<div className="absolute flex items-center justify-center right-2" onClick={ onCloseClick } onMouseDownCapture={ onMouseDown }>
|
||||
<FaTimes className="fa-icon w-[12px] h-[12px]" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
NitroCardHeader.displayName = 'NitroCardHeader';
|
||||
|
||||
const NitroCardContent = forwardRef<HTMLDivElement, DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { className = null, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'overflow-auto',
|
||||
className
|
||||
) }
|
||||
{ ...rest } />
|
||||
);
|
||||
});
|
||||
|
||||
NitroCardContent.displayName = 'NitroCardContent';
|
||||
|
||||
const NitroCardTabsMain = forwardRef<HTMLDivElement, {
|
||||
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { className = null, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'justify-center gap-1 flex',
|
||||
className)
|
||||
}
|
||||
{ ...rest } />
|
||||
)
|
||||
});
|
||||
|
||||
NitroCardTabsMain.displayName = 'NitroCardTabsMain';
|
||||
|
||||
const NitroCardTabsItem = forwardRef<HTMLDivElement, {
|
||||
isActive?: boolean;
|
||||
count?: number;
|
||||
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { isActive = false, count = 0, className = null, children = null, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'overflow-hidden relative cursor-pointer rounded-t-lg flex',
|
||||
isActive && 'active',
|
||||
className)
|
||||
}
|
||||
{ ...rest }>
|
||||
<div className="flex items-center justify-center shrink-0">
|
||||
{ children }
|
||||
</div>
|
||||
{ (count > 0) &&
|
||||
<NitroItemCountBadge count={ count } /> }
|
||||
</div>
|
||||
)
|
||||
});
|
||||
|
||||
NitroCardTabsItem.displayName = 'NitroCardTabsItem';
|
||||
|
||||
export const NitroCard = {
|
||||
Main: NitroCardMain,
|
||||
Header: NitroCardHeader,
|
||||
Content: NitroCardContent
|
||||
};
|
||||
|
||||
export const NitroCardTabs = {
|
||||
Main: NitroCardTabsMain,
|
||||
Item: NitroCardTabsItem
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import { DetailedHTMLProps, forwardRef, HTMLAttributes, PropsWithChildren } from 'react';
|
||||
import { classNames } from '../common';
|
||||
|
||||
const classes = {
|
||||
base: 'top-2 right-2 py-0.5 px-[3px] z-[1] rounded border',
|
||||
themes: {
|
||||
'primary': 'border-black bg-red-700'
|
||||
}
|
||||
}
|
||||
|
||||
export const NitroItemCountBadge = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
theme?: 'primary';
|
||||
count: number;
|
||||
}> & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { theme = 'primary', count = 0, className = null, children = null, ...rest } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
classes.base,
|
||||
classes.themes[theme],
|
||||
className
|
||||
) }
|
||||
{ ...rest }>
|
||||
{ count }
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
NitroItemCountBadge.displayName = 'NitroItemCountBadge';
|
|
@ -0,0 +1,2 @@
|
|||
export * from './NitroCard';
|
||||
export * from './NitroItemCountBadge';
|
|
@ -1,22 +1,30 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
|
||||
const { generateShades } = require('./css-utils/CSSColorUtils');
|
||||
|
||||
const colors = {
|
||||
'toolbar': 'rgba(28, 28, 32, .95)'
|
||||
'toolbar': '#555555',
|
||||
};
|
||||
|
||||
const boxShadow = {
|
||||
'inner1px': 'inset 0 0 0 1px rgba(255,255,255,.3)'
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: [ 'Ubuntu', 'sans-serif' ],
|
||||
},
|
||||
colors,
|
||||
colors: generateShades(colors),
|
||||
boxShadow,
|
||||
width: {
|
||||
'navigator': '420px'
|
||||
},
|
||||
height: {
|
||||
'toolbar': '55px'
|
||||
'toolbar': '55px',
|
||||
'navigator': '440px'
|
||||
},
|
||||
zIndex: {
|
||||
'toolbar': ''
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "react-jsx",
|
||||
"paths": {
|
||||
"@layout/*": ["layout/*"],
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src",
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
import react from '@vitejs/plugin-react';
|
||||
import { resolve } from 'path';
|
||||
import { defineConfig } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [ react() ],
|
||||
plugins: [ react(), tsconfigPaths() ],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
|
|
21
yarn.lock
21
yarn.lock
|
@ -1261,7 +1261,7 @@ debug@^3.2.7:
|
|||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
|
@ -1912,6 +1912,11 @@ globby@^11.1.0:
|
|||
merge2 "^1.4.1"
|
||||
slash "^3.0.0"
|
||||
|
||||
globrex@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
|
@ -3242,6 +3247,11 @@ ts-interface-checker@^0.1.9:
|
|||
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
||||
|
||||
tsconfck@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.0.3.tgz#d9bda0e87d05b1c360e996c9050473c7e6f8084f"
|
||||
integrity sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==
|
||||
|
||||
tsconfig-paths@^3.15.0:
|
||||
version "3.15.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4"
|
||||
|
@ -3373,6 +3383,15 @@ util-deprecate@^1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
vite-tsconfig-paths@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz#321f02e4b736a90ff62f9086467faf4e2da857a9"
|
||||
integrity sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
globrex "^0.1.2"
|
||||
tsconfck "^3.0.3"
|
||||
|
||||
vite@^5.1.6:
|
||||
version "5.2.7"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.7.tgz#e1b8a985eb54fcb9467d7f7f009d87485016df6e"
|
||||
|
|
Loading…
Reference in New Issue