Merge branch 'dev' into feature/guide-tool
7
.env.example
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
BROWSER=none
|
||||||
|
GENERATE_SOURCEMAP=false
|
||||||
|
REACT_APP_CONFIG_URLS=/renderer-config.json,/ui-config.json
|
||||||
|
REACT_APP_SOCKET_URL=wss://ws.server.com:2096
|
||||||
|
REACT_APP_ASSET_URL=https://nitro.server.com
|
||||||
|
REACT_APP_IMAGE_LIBRARY_URL=https://swf.server.com/c_images/
|
||||||
|
REACT_APP_HOF_FURNI_URL=https://swf.server.com/dcr/hof_furni
|
34
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: dedicated-server
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [16.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
cache: "yarn"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
yarn remove @nitrots/nitro-renderer
|
||||||
|
yarn add git+https://git@git.krews.org/nitro/nitro-renderer#dev
|
||||||
|
yarn install
|
||||||
|
- name: Build Nitro
|
||||||
|
run: |
|
||||||
|
yarn build
|
||||||
|
- name: Archive Artifacts
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
build
|
49
.github/workflows/deploy.yml
vendored
@ -1,49 +0,0 @@
|
|||||||
name: Deploy Bundle@dev
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ dev ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: self-hosted
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [14.x]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- name: Cache dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: '~/.npm'
|
|
||||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-node-
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
npm uninstall @nitrots/nitro-renderer
|
|
||||||
npm install git+https://git@git.krews.org/nitro/nitro-renderer#dev
|
|
||||||
- name: Build Nitro
|
|
||||||
run: |
|
|
||||||
npm run build
|
|
||||||
- name: Archive Artifacts
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
build
|
|
||||||
- name: Deploy Artifacts
|
|
||||||
uses: easingthemes/ssh-deploy@main
|
|
||||||
env:
|
|
||||||
REMOTE_HOST: ${{ secrets.HOST }}
|
|
||||||
REMOTE_PORT: ${{ secrets.PORT }}
|
|
||||||
REMOTE_USER: ${{ secrets.USERNAME }}
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSHKEY }}
|
|
||||||
SOURCE: "build/"
|
|
||||||
TARGET: ${{ secrets.TARGET }}
|
|
29
.gitignore
vendored
@ -1,20 +1,7 @@
|
|||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# compiled output
|
|
||||||
/dist
|
/dist
|
||||||
/tmp
|
/tmp
|
||||||
/out-tsc
|
/out-tsc
|
||||||
# Only exists if Bazel was run
|
|
||||||
/bazel-out
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
/node_modules
|
||||||
|
|
||||||
# profiling files
|
|
||||||
chrome-profiler-events*.json
|
|
||||||
speed-measure-plugin*.json
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
/.idea
|
/.idea
|
||||||
.project
|
.project
|
||||||
.classpath
|
.classpath
|
||||||
@ -22,29 +9,23 @@ speed-measure-plugin*.json
|
|||||||
*.launch
|
*.launch
|
||||||
.settings/
|
.settings/
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
# IDE - VSCode
|
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
.history/*
|
.history/*
|
||||||
|
|
||||||
# misc
|
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/connect.lock
|
/connect.lock
|
||||||
/coverage
|
/coverage
|
||||||
/libpeerconnection.log
|
*.log
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
.git
|
.git
|
||||||
|
|
||||||
# System Files
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# Nitro
|
||||||
/build
|
/build
|
||||||
*.zip
|
*.zip
|
||||||
|
.env
|
||||||
|
public/renderer-config.json
|
||||||
|
public/ui-config.json
|
||||||
|
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.scss
|
19384
package-lock.json
generated
17
package.json
@ -5,16 +5,19 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "craco start",
|
"start": "craco start",
|
||||||
"build": "craco --max_old_space_size=8048 build",
|
"build": "craco --max_old_space_size=8048 build",
|
||||||
"build:prod": "npm uninstall @nitrots/nitro-renderer && npm i git+https://git@git.krews.org/nitro/nitro-renderer#dev && npm i && npm run build",
|
"build:prod": "npx browserslist@latest --update-db && yarn install && yarn build",
|
||||||
"test": "craco test",
|
"test": "craco test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@craco/craco": "^6.3.0",
|
"@craco/craco": "^6.3.0",
|
||||||
"@nitrots/nitro-renderer": "file:../nitro-renderer",
|
"@fortawesome/fontawesome-svg-core": "^1.2.36",
|
||||||
|
"@fortawesome/free-solid-svg-icons": "^5.15.4",
|
||||||
|
"@fortawesome/react-fontawesome": "^0.1.16",
|
||||||
|
"@nitrots/nitro-renderer": "^1.1.4",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^6.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-bootstrap": "^2.0.0-alpha.2",
|
"react-bootstrap": "^2.0.0-alpha.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
@ -23,20 +26,16 @@
|
|||||||
"react-transition-group": "^4.4.2",
|
"react-transition-group": "^4.4.2",
|
||||||
"react-virtualized": "^9.22.3",
|
"react-virtualized": "^9.22.3",
|
||||||
"react-youtube": "^7.13.1",
|
"react-youtube": "^7.13.1",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5"
|
||||||
"web-vitals": "^1.1.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
|
||||||
"@testing-library/react": "^11.2.7",
|
|
||||||
"@testing-library/user-event": "^12.8.3",
|
|
||||||
"@types/jest": "^26.0.24",
|
|
||||||
"@types/node": "^12.20.19",
|
"@types/node": "^12.20.19",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"@types/react-slider": "^1.3.1",
|
"@types/react-slider": "^1.3.1",
|
||||||
"@types/react-transition-group": "^4.4.2",
|
"@types/react-transition-group": "^4.4.2",
|
||||||
"@types/react-virtualized": "^9.21.13",
|
"@types/react-virtualized": "^9.21.13",
|
||||||
|
"@types/styled-components": "^5.1.15",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.29.1"
|
"@typescript-eslint/eslint-plugin": "^4.29.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
post-install.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { request as httpsRequest } from 'https';
|
||||||
|
|
||||||
|
function install()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
params['packageName'] = process.env.npm_package_name;
|
||||||
|
params['packageVersion'] = process.env.npm_package_version;
|
||||||
|
|
||||||
|
const data = JSON.stringify(params);
|
||||||
|
const request = httpsRequest({
|
||||||
|
hostname: 'install.nitrots.co',
|
||||||
|
port: 443,
|
||||||
|
path: '/collect',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Content-Length': data.length
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
request.write(data);
|
||||||
|
request.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
install();
|
@ -13,9 +13,13 @@
|
|||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root" class="w-100 h-100"></div>
|
<div id="root" class="w-100 h-100"></div>
|
||||||
<script>
|
<script>
|
||||||
var NitroConfig = {
|
const NitroConfig = {
|
||||||
configurationUrls: [ '/renderer-config.json', '/ui-config.json' ],
|
"config.urls": ("%REACT_APP_CONFIG_URLS%").split(','),
|
||||||
sso: (new URLSearchParams(window.location.search).get('sso') || null)
|
"sso.ticket": (new URLSearchParams(window.location.search).get('sso') || null),
|
||||||
|
"socket.url": "%REACT_APP_SOCKET_URL%",
|
||||||
|
"asset.url": "%REACT_APP_ASSET_URL%",
|
||||||
|
"image.library.url": "%REACT_APP_IMAGE_LIBRARY_URL%",
|
||||||
|
"hof.furni.url": "%REACT_APP_HOF_FURNI_URL%"
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"socket.url": "wss://ws.nitrots.co:2096",
|
"socket.url": "",
|
||||||
"asset.url": "https://nitro.nitrots.co",
|
"asset.url": "",
|
||||||
"image.library.url": "https://swf.nitrots.co/c_images/",
|
"image.library.url": "",
|
||||||
"hof.furni.url": "https://swf.nitrots.co/dcr/hof_furni",
|
"hof.furni.url": "",
|
||||||
"images.url": "${asset.url}/images",
|
"images.url": "${asset.url}/images",
|
||||||
"gamedata.url": "${asset.url}/gamedata",
|
"gamedata.url": "${asset.url}/gamedata",
|
||||||
"sounds.url": "${asset.url}/sounds/%sample%.mp3",
|
"sounds.url": "${asset.url}/sounds/%sample%.mp3",
|
||||||
@ -21,9 +21,7 @@
|
|||||||
"pet.asset.url": "${asset.url}/bundled/pet/%libname%.nitro",
|
"pet.asset.url": "${asset.url}/bundled/pet/%libname%.nitro",
|
||||||
"generic.asset.url": "${asset.url}/bundled/generic/%libname%.nitro",
|
"generic.asset.url": "${asset.url}/bundled/generic/%libname%.nitro",
|
||||||
"badge.asset.url": "${image.library.url}album1584/%badgename%.gif",
|
"badge.asset.url": "${image.library.url}album1584/%badgename%.gif",
|
||||||
"badge.asset.group.url": "https://cdn.ironhotel.biz/group-badge/%badgedata%.gif",
|
"badge.asset.grouparts.url": "${image.library.url}Badgeparts/badgepart_%part%.png",
|
||||||
"badge.asset.group.external.url": "",
|
|
||||||
"badge.asset.grouparts.url": "https://cdn.ironhotel.biz/static_iron_active/c_images/Badgeparts/badgepart_%part%.png",
|
|
||||||
"furni.rotation.bounce.steps": 20,
|
"furni.rotation.bounce.steps": 20,
|
||||||
"furni.rotation.bounce.height": 0.0625,
|
"furni.rotation.bounce.height": 0.0625,
|
||||||
"enable.avatar.arrow": false,
|
"enable.avatar.arrow": false,
|
||||||
@ -101,20 +99,8 @@
|
|||||||
"elephants"
|
"elephants"
|
||||||
],
|
],
|
||||||
"preload.assets.urls": [
|
"preload.assets.urls": [
|
||||||
"${images.url}/additions/user_blowkiss.png",
|
"${asset.url}/bundled/generic/avatar_additions.nitro",
|
||||||
"${images.url}/additions/user_idle_left_1.png",
|
"${asset.url}/bundled/generic/floor_editor.nitro",
|
||||||
"${images.url}/additions/user_idle_left_2.png",
|
|
||||||
"${images.url}/additions/user_idle_right_1.png",
|
|
||||||
"${images.url}/additions/user_idle_right_2.png",
|
|
||||||
"${images.url}/additions/user_muted.png",
|
|
||||||
"${images.url}/additions/user_muted_small.png",
|
|
||||||
"${images.url}/additions/user_typing.png",
|
|
||||||
"${images.url}/additions/number_1.png",
|
|
||||||
"${images.url}/additions/number_2.png",
|
|
||||||
"${images.url}/additions/number_3.png",
|
|
||||||
"${images.url}/additions/number_4.png",
|
|
||||||
"${images.url}/additions/number_5.png",
|
|
||||||
"${images.url}/additions/pet_experience_bubble.png",
|
|
||||||
"${images.url}/loading_icon.png",
|
"${images.url}/loading_icon.png",
|
||||||
"${images.url}/clear_icon.png",
|
"${images.url}/clear_icon.png",
|
||||||
"${images.url}/big_arrow.png"
|
"${images.url}/big_arrow.png"
|
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"image.library.notifications.url": "${image.library.url}notifications/%image%.png",
|
"image.library.notifications.url": "${image.library.url}notifications/%image%.png",
|
||||||
"achievements.images.url": "${image.library.url}Quests/%image%.png",
|
"achievements.images.url": "${image.library.url}Quests/%image%.png",
|
||||||
"camera.url": "https://nitro.nitrots.co/camera",
|
"camera.url": "https://camera.com",
|
||||||
"thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png",
|
"thumbnails.url": "https://camera.com/thumbnail/%thumbnail%.png",
|
||||||
"url.prefix": "http://localhost:3000",
|
"url.prefix": "https://website.com",
|
||||||
"floorplan.tile.url": "${asset.url}/floorplan-editor/tiles.json",
|
"floorplan.tile.url": "${asset.url}/floorplan-editor/tiles.json",
|
||||||
"habbopages.url": "${url.prefix}/",
|
"habbopages.url": "${url.prefix}/",
|
||||||
"group.homepage.url": "${url.prefix}/groups/%groupid%/id",
|
"group.homepage.url": "${url.prefix}/groups/%groupid%/id",
|
||||||
"guide.help.alpha.groupid": 0,
|
"guide.help.alpha.groupid": 0,
|
||||||
"chat.viewer.height.percentage": 0.40,
|
"chat.viewer.height.percentage": 0.40,
|
||||||
"widget.dimmer.colorwheel": false,
|
"widget.dimmer.colorwheel": false,
|
||||||
|
"avatar.wardrobe.max.slots": 10,
|
||||||
"hotelview": {
|
"hotelview": {
|
||||||
"widgets": {
|
"widgets": {
|
||||||
"slot.1.widget": "promoarticle",
|
"slot.1.widget": "promoarticle",
|
||||||
@ -46,9 +47,17 @@
|
|||||||
"system.currency.types": [
|
"system.currency.types": [
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
5,
|
5
|
||||||
101
|
|
||||||
],
|
],
|
||||||
|
"hc.center": {
|
||||||
|
"benefits.info": true,
|
||||||
|
"payday.info": true,
|
||||||
|
"gift.info": true,
|
||||||
|
"benefits.habbopage": "habboclub",
|
||||||
|
"payday.habbopage": "hcpayday",
|
||||||
|
"catalog.buy": "habbo_club",
|
||||||
|
"catalog.gifts": "club_gifts"
|
||||||
|
},
|
||||||
"currency.display.number.short": false,
|
"currency.display.number.short": false,
|
||||||
"currency.asset.icon.url": "${images.url}/wallet/%type%.png",
|
"currency.asset.icon.url": "${images.url}/wallet/%type%.png",
|
||||||
"catalog.asset.url": "${image.library.url}catalogue",
|
"catalog.asset.url": "${image.library.url}catalogue",
|
25
src/App.scss
@ -17,8 +17,8 @@ $grid-active-border-color: $white;
|
|||||||
|
|
||||||
$toolbar-height: 55px;
|
$toolbar-height: 55px;
|
||||||
|
|
||||||
$achievement-width: 350px;
|
$achievement-width: 375px;
|
||||||
$achievement-height: 370px;
|
$achievement-height: 425px;
|
||||||
|
|
||||||
$avatar-editor-width: 620px;
|
$avatar-editor-width: 620px;
|
||||||
$avatar-editor-height: 374px;
|
$avatar-editor-height: 374px;
|
||||||
@ -50,16 +50,33 @@ $chat-history-height: 300px;
|
|||||||
$friends-list-width: 250px;
|
$friends-list-width: 250px;
|
||||||
$friends-list-height: 300px;
|
$friends-list-height: 300px;
|
||||||
|
|
||||||
$help-width: 275px;
|
$help-width: 450px;
|
||||||
$help-height: 450px;
|
$help-height: 250px;
|
||||||
|
|
||||||
$nitropedia-width: 400px;
|
$nitropedia-width: 400px;
|
||||||
$nitropedia-height: 400px;
|
$nitropedia-height: 400px;
|
||||||
|
|
||||||
|
$messenger-width: 500px;
|
||||||
|
$messenger-height: 370px;
|
||||||
|
|
||||||
|
$marketplace-post-offer-width: 430px;
|
||||||
|
$marketplace-post-offer-height: 250px;
|
||||||
|
|
||||||
|
$camera-editor-width: 600px;
|
||||||
|
$camera-editor-height: 500px;
|
||||||
|
|
||||||
|
$camera-checkout-width: 350px;
|
||||||
|
|
||||||
|
$room-info-width: 325px;
|
||||||
|
|
||||||
|
$nitro-mod-tools-width: 175px;
|
||||||
|
|
||||||
.nitro-app {
|
.nitro-app {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@import './common';
|
||||||
@import "./layout/Layout";
|
@import "./layout/Layout";
|
||||||
|
@import './components';
|
||||||
@import "./views/Styles";
|
@import "./views/Styles";
|
||||||
|
20
src/App.tsx
@ -1,13 +1,14 @@
|
|||||||
import { ConfigurationEvent, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer';
|
import { ConfigurationEvent, HabboWebTools, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
|
import { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
|
||||||
|
import { Base } from './common';
|
||||||
|
import { LoadingView } from './components/loading/LoadingView';
|
||||||
|
import { MainView } from './components/main/MainView';
|
||||||
import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event';
|
import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event';
|
||||||
import { useLocalizationEvent } from './hooks/events/nitro/localization/localization-event';
|
import { useLocalizationEvent } from './hooks/events/nitro/localization/localization-event';
|
||||||
import { dispatchMainEvent, useMainEvent } from './hooks/events/nitro/main-event';
|
import { dispatchMainEvent, useMainEvent } from './hooks/events/nitro/main-event';
|
||||||
import { useRoomEngineEvent } from './hooks/events/nitro/room/room-engine-event';
|
import { useRoomEngineEvent } from './hooks/events/nitro/room/room-engine-event';
|
||||||
import { TransitionAnimation, TransitionAnimationTypes } from './layout';
|
import { TransitionAnimation, TransitionAnimationTypes } from './layout';
|
||||||
import { LoadingView } from './views/loading/LoadingView';
|
|
||||||
import { MainView } from './views/main/MainView';
|
|
||||||
|
|
||||||
export const App: FC<{}> = props =>
|
export const App: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
@ -68,6 +69,8 @@ export const App: FC<{}> = props =>
|
|||||||
setMessage('Finishing Up');
|
setMessage('Finishing Up');
|
||||||
|
|
||||||
GetNitroInstance().init();
|
GetNitroInstance().init();
|
||||||
|
|
||||||
|
if(LegacyExternalInterface.available) LegacyExternalInterface.call('legacyTrack', 'authentication', 'authok', []);
|
||||||
return;
|
return;
|
||||||
case NitroCommunicationDemoEvent.CONNECTION_ERROR:
|
case NitroCommunicationDemoEvent.CONNECTION_ERROR:
|
||||||
setIsError(true);
|
setIsError(true);
|
||||||
@ -79,7 +82,7 @@ export const App: FC<{}> = props =>
|
|||||||
setIsError(true);
|
setIsError(true);
|
||||||
setMessage('Connection Error');
|
setMessage('Connection Error');
|
||||||
|
|
||||||
LegacyExternalInterface.call('disconnect', -1, 'client.init.handshake.fail');
|
HabboWebTools.send(-1, 'client.init.handshake.fail');
|
||||||
return;
|
return;
|
||||||
case RoomEngineEvent.ENGINE_INITIALIZED:
|
case RoomEngineEvent.ENGINE_INITIALIZED:
|
||||||
setIsReady(true);
|
setIsReady(true);
|
||||||
@ -125,12 +128,13 @@ export const App: FC<{}> = props =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nitro-app overflow-hidden">
|
<Base fit overflow="hidden">
|
||||||
<div id="nitro-alerts-container" />
|
{ (!isReady || isError) &&
|
||||||
{ (!isReady || isError) && <LoadingView isError={ isError } message={ message } /> }
|
<LoadingView isError={ isError } message={ message } /> }
|
||||||
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady && !isError) }>
|
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ (isReady && !isError) }>
|
||||||
<MainView />
|
<MainView />
|
||||||
</TransitionAnimation>
|
</TransitionAnimation>
|
||||||
</div>
|
<Base id="draggable-windows-container" />
|
||||||
|
</Base>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,7 @@ import { GetNitroInstance } from './GetNitroInstance';
|
|||||||
|
|
||||||
export function CreateLinkEvent(link: string): void
|
export function CreateLinkEvent(link: string): void
|
||||||
{
|
{
|
||||||
|
link = (link.startsWith('event:') ? link.substring(6) : link);
|
||||||
|
|
||||||
GetNitroInstance().createLinkEvent(link);
|
GetNitroInstance().createLinkEvent(link);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { IFurnitureData, IGetImageListener, NitroEvent, NitroRenderTexture, PetFigureData, RoomObjectCategory, RoomObjectVariable, RoomSessionPresentEvent, RoomWidgetEnum, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
import { IFurnitureData, IGetImageListener, NitroEvent, NitroRenderTexture, PetFigureData, RoomObjectCategory, RoomObjectVariable, RoomSessionPresentEvent, RoomWidgetEnum, TextureUtils, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { GetSessionDataManager, IsOwnerOfFurniture } from '../../..';
|
import { GetSessionDataManager, IsOwnerOfFurniture } from '../../..';
|
||||||
import { GetRoomEngine, LocalizeText } from '../../../..';
|
import { GetRoomEngine, LocalizeText } from '../../../..';
|
||||||
import { ProductTypeEnum } from '../../../../../views/catalog/common/ProductTypeEnum';
|
import { ProductTypeEnum } from '../../../../../components/catalog/common/ProductTypeEnum';
|
||||||
import { RoomWidgetUpdateEvent, RoomWidgetUpdatePresentDataEvent } from '../events';
|
import { RoomWidgetUpdateEvent, RoomWidgetUpdatePresentDataEvent } from '../events';
|
||||||
import { RoomWidgetFurniToWidgetMessage, RoomWidgetPresentOpenMessage } from '../messages';
|
import { RoomWidgetFurniToWidgetMessage, RoomWidgetPresentOpenMessage } from '../messages';
|
||||||
import { RoomWidgetMessage } from '../messages/RoomWidgetMessage';
|
import { RoomWidgetMessage } from '../messages/RoomWidgetMessage';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NitroEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionDanceEvent, RoomSessionUserDataUpdateEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
|
import { NitroEvent, RoomEngineUseProductEvent, RoomObjectCategory, RoomObjectType, RoomObjectVariable, RoomSessionDanceEvent, RoomSessionPetStatusUpdateEvent, RoomSessionUserDataUpdateEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
|
||||||
import { GetRoomEngine, GetRoomSession, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..';
|
import { GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..';
|
||||||
import { FurniCategory } from '../../../../../views/inventory/common/FurniCategory';
|
import { FurniCategory } from '../../../../../components/inventory/common/FurniCategory';
|
||||||
import { RoomWidgetAvatarInfoEvent, RoomWidgetUpdateDanceStatusEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateUserDataEvent, RoomWidgetUseProductBubbleEvent, UseProductItem } from '../events';
|
import { RoomWidgetAvatarInfoEvent, RoomWidgetUpdateDanceStatusEvent, RoomWidgetUpdateEvent, RoomWidgetUpdateUserDataEvent, RoomWidgetUseProductBubbleEvent, UseProductItem } from '../events';
|
||||||
import { RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUseProductMessage, RoomWidgetUserActionMessage } from '../messages';
|
import { RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetRoomObjectMessage, RoomWidgetUseProductMessage, RoomWidgetUserActionMessage } from '../messages';
|
||||||
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
||||||
@ -19,7 +19,7 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
|
|
||||||
let isDancing = false;
|
let isDancing = false;
|
||||||
|
|
||||||
const userData = GetRoomSession().userDataManager.getUserData(GetSessionDataManager().userId);
|
const userData = this.container.roomSession.userDataManager.getUserData(GetSessionDataManager().userId);
|
||||||
|
|
||||||
if(userData && (userData.roomIndex === danceEvent.roomIndex)) isDancing = (danceEvent.danceId !== 0);
|
if(userData && (userData.roomIndex === danceEvent.roomIndex)) isDancing = (danceEvent.danceId !== 0);
|
||||||
|
|
||||||
@ -30,6 +30,9 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM:
|
case RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM:
|
||||||
this.processUsableRoomObject((event as RoomEngineUseProductEvent).objectId);
|
this.processUsableRoomObject((event as RoomEngineUseProductEvent).objectId);
|
||||||
return;
|
return;
|
||||||
|
case RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE:
|
||||||
|
this.processRoomSessionPetStatusUpdateEvent((event as RoomSessionPetStatusUpdateEvent));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,28 +47,39 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
case RoomWidgetRoomObjectMessage.GET_OWN_CHARACTER_INFO:
|
case RoomWidgetRoomObjectMessage.GET_OWN_CHARACTER_INFO:
|
||||||
this.processOwnCharacterInfo();
|
this.processOwnCharacterInfo();
|
||||||
break;
|
break;
|
||||||
|
case RoomWidgetUserActionMessage.START_NAME_CHANGE:
|
||||||
|
// habbo help - start name change
|
||||||
|
break;
|
||||||
|
case RoomWidgetUserActionMessage.REQUEST_PET_UPDATE:
|
||||||
|
break;
|
||||||
|
case RoomWidgetUseProductMessage.PET_PRODUCT: {
|
||||||
|
const productMessage = (message as RoomWidgetUseProductMessage);
|
||||||
|
|
||||||
|
this.container.roomSession.usePetProduct(productMessage.objectId, productMessage.petId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RoomWidgetUserActionMessage.HARVEST_PET:
|
||||||
|
this.container.roomSession.harvestPet(userId);
|
||||||
|
break;
|
||||||
|
case RoomWidgetUserActionMessage.COMPOST_PLANT:
|
||||||
|
this.container.roomSession.compostPlant(userId);
|
||||||
|
break;
|
||||||
case RoomWidgetDanceMessage.DANCE: {
|
case RoomWidgetDanceMessage.DANCE: {
|
||||||
const danceMessage = (message as RoomWidgetDanceMessage);
|
const danceMessage = (message as RoomWidgetDanceMessage);
|
||||||
|
|
||||||
GetRoomSession().sendDanceMessage(danceMessage.style);
|
this.container.roomSession.sendDanceMessage(danceMessage.style);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RoomWidgetAvatarExpressionMessage.AVATAR_EXPRESSION: {
|
case RoomWidgetAvatarExpressionMessage.AVATAR_EXPRESSION: {
|
||||||
const expressionMessage = (message as RoomWidgetAvatarExpressionMessage);
|
const expressionMessage = (message as RoomWidgetAvatarExpressionMessage);
|
||||||
|
|
||||||
GetRoomSession().sendExpressionMessage(expressionMessage.animation.ordinal)
|
this.container.roomSession.sendExpressionMessage(expressionMessage.animation.ordinal)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RoomWidgetChangePostureMessage.CHANGE_POSTURE: {
|
case RoomWidgetChangePostureMessage.CHANGE_POSTURE: {
|
||||||
const postureMessage = (message as RoomWidgetChangePostureMessage);
|
const postureMessage = (message as RoomWidgetChangePostureMessage);
|
||||||
|
|
||||||
GetRoomSession().sendPostureMessage(postureMessage.posture);
|
this.container.roomSession.sendPostureMessage(postureMessage.posture);
|
||||||
break;
|
|
||||||
}
|
|
||||||
case RoomWidgetUseProductMessage.PET_PRODUCT: {
|
|
||||||
const productMessage = (message as RoomWidgetUseProductMessage);
|
|
||||||
|
|
||||||
GetRoomSession().usePetProduct(productMessage.objectId, productMessage.petId);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -78,9 +92,11 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
const userId = GetSessionDataManager().userId;
|
const userId = GetSessionDataManager().userId;
|
||||||
const userName = GetSessionDataManager().userName;
|
const userName = GetSessionDataManager().userName;
|
||||||
const allowNameChange = GetSessionDataManager().canChangeName;
|
const allowNameChange = GetSessionDataManager().canChangeName;
|
||||||
const userData = GetRoomSession().userDataManager.getUserData(userId);
|
const userData = this.container.roomSession.userDataManager.getUserData(userId);
|
||||||
|
|
||||||
if(userData) this.container.eventDispatcher.dispatchEvent(new RoomWidgetAvatarInfoEvent(userId, userName, userData.type, userData.roomIndex, allowNameChange));
|
if(!userData) return;
|
||||||
|
|
||||||
|
this.container.eventDispatcher.dispatchEvent(new RoomWidgetAvatarInfoEvent(userId, userName, userData.type, userData.roomIndex, allowNameChange));
|
||||||
}
|
}
|
||||||
|
|
||||||
private processUsableRoomObject(objectId: number): void
|
private processUsableRoomObject(objectId: number): void
|
||||||
@ -120,24 +136,24 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
{
|
{
|
||||||
if(userData.ownerId === ownerId)
|
if(userData.ownerId === ownerId)
|
||||||
{
|
{
|
||||||
if(userData.hasSaddle && (specialType === FurniCategory._Str_6096)) replace = true;
|
if(userData.hasSaddle && (specialType === FurniCategory.PET_SADDLE)) replace = true;
|
||||||
|
|
||||||
const figureParts = userData.figure.split(' ');
|
const figureParts = userData.figure.split(' ');
|
||||||
const figurePart = (figureParts.length ? parseInt(figureParts[0]) : -1);
|
const figurePart = (figureParts.length ? parseInt(figureParts[0]) : -1);
|
||||||
|
|
||||||
if(figurePart === part)
|
if(figurePart === part)
|
||||||
{
|
{
|
||||||
if(specialType === FurniCategory._Str_6915)
|
if(specialType === FurniCategory.MONSTERPLANT_REVIVAL)
|
||||||
{
|
{
|
||||||
if(!userData.canRevive) continue;
|
if(!userData.canRevive) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(specialType === FurniCategory._Str_8726)
|
if(specialType === FurniCategory.MONSTERPLANT_REBREED)
|
||||||
{
|
{
|
||||||
if((userData.petLevel < 7) || userData.canRevive || userData.canBreed) continue;
|
if((userData.petLevel < 7) || userData.canRevive || userData.canBreed) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(specialType === FurniCategory._Str_9449)
|
if(specialType === FurniCategory.MONSTERPLANT_FERTILIZE)
|
||||||
{
|
{
|
||||||
if((userData.petLevel >= 7) || userData.canRevive) continue;
|
if((userData.petLevel >= 7) || userData.canRevive) continue;
|
||||||
}
|
}
|
||||||
@ -151,6 +167,11 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
if(useProductBubbles.length) this.container.eventDispatcher.dispatchEvent(new RoomWidgetUseProductBubbleEvent(RoomWidgetUseProductBubbleEvent.USE_PRODUCT_BUBBLES, useProductBubbles));
|
if(useProductBubbles.length) this.container.eventDispatcher.dispatchEvent(new RoomWidgetUseProductBubbleEvent(RoomWidgetUseProductBubbleEvent.USE_PRODUCT_BUBBLES, useProductBubbles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private processRoomSessionPetStatusUpdateEvent(event: RoomSessionPetStatusUpdateEvent): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public get type(): string
|
public get type(): string
|
||||||
{
|
{
|
||||||
return RoomWidgetEnum.AVATAR_INFO;
|
return RoomWidgetEnum.AVATAR_INFO;
|
||||||
@ -162,18 +183,29 @@ export class RoomWidgetAvatarInfoHandler extends RoomWidgetHandler
|
|||||||
RoomSessionUserDataUpdateEvent.USER_DATA_UPDATED,
|
RoomSessionUserDataUpdateEvent.USER_DATA_UPDATED,
|
||||||
RoomSessionDanceEvent.RSDE_DANCE,
|
RoomSessionDanceEvent.RSDE_DANCE,
|
||||||
RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY,
|
RoomEngineUseProductEvent.USE_PRODUCT_FROM_INVENTORY,
|
||||||
RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM
|
RoomEngineUseProductEvent.USE_PRODUCT_FROM_ROOM,
|
||||||
|
RoomSessionPetStatusUpdateEvent.PET_STATUS_UPDATE
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserNameUpdateEvent.UNUE_NAME_UPDATED
|
||||||
|
// RoomSessionNestBreedingSuccessEvent.RSPFUE_NEST_BREEDING_SUCCESS
|
||||||
|
// RoomSessionPetLevelUpdateEvent.RSPLUE_PET_LEVEL_UPDATE
|
||||||
|
|
||||||
public get messageTypes(): string[]
|
public get messageTypes(): string[]
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
RoomWidgetRoomObjectMessage.GET_OWN_CHARACTER_INFO,
|
RoomWidgetRoomObjectMessage.GET_OWN_CHARACTER_INFO,
|
||||||
|
RoomWidgetUserActionMessage.START_NAME_CHANGE,
|
||||||
|
RoomWidgetUserActionMessage.REQUEST_PET_UPDATE,
|
||||||
|
RoomWidgetUseProductMessage.PET_PRODUCT,
|
||||||
|
RoomWidgetUserActionMessage.REQUEST_BREED_PET,
|
||||||
|
RoomWidgetUserActionMessage.HARVEST_PET,
|
||||||
|
RoomWidgetUserActionMessage.REVIVE_PET,
|
||||||
|
RoomWidgetUserActionMessage.COMPOST_PLANT,
|
||||||
RoomWidgetDanceMessage.DANCE,
|
RoomWidgetDanceMessage.DANCE,
|
||||||
RoomWidgetAvatarExpressionMessage.AVATAR_EXPRESSION,
|
RoomWidgetAvatarExpressionMessage.AVATAR_EXPRESSION,
|
||||||
RoomWidgetChangePostureMessage.CHANGE_POSTURE,
|
RoomWidgetChangePostureMessage.CHANGE_POSTURE,
|
||||||
RoomWidgetUseProductMessage.PET_PRODUCT
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { AvatarExpressionEnum, HabboClubLevelEnum, NitroEvent, RoomControllerLevel, RoomSessionChatEvent, RoomSettingsComposer, RoomWidgetEnum, RoomZoomEvent, TextureUtils } from '@nitrots/nitro-renderer';
|
import { AvatarExpressionEnum, HabboClubLevelEnum, NitroEvent, RoomControllerLevel, RoomRotatingEffect, RoomSessionChatEvent, RoomSettingsComposer, RoomShakingEffect, RoomWidgetEnum, RoomZoomEvent, TextureUtils } from '@nitrots/nitro-renderer';
|
||||||
import { GetConfiguration, GetNitroInstance } from '../../..';
|
import { GetConfiguration, GetNitroInstance } from '../../..';
|
||||||
import { GetRoomEngine, GetSessionDataManager } from '../../../..';
|
import { GetRoomEngine, GetSessionDataManager, LocalizeText } from '../../../..';
|
||||||
import { FloorplanEditorEvent } from '../../../../../events/floorplan-editor/FloorplanEditorEvent';
|
import { FloorplanEditorEvent } from '../../../../../events/floorplan-editor/FloorplanEditorEvent';
|
||||||
import { dispatchUiEvent } from '../../../../../hooks';
|
import { dispatchUiEvent } from '../../../../../hooks';
|
||||||
import { SendMessageHook } from '../../../../../hooks/messages';
|
import { SendMessageHook } from '../../../../../hooks/messages';
|
||||||
|
import { NotificationUtilities } from '../../../../../views/notification-center/common/NotificationUtilities';
|
||||||
import { RoomWidgetFloodControlEvent, RoomWidgetUpdateEvent } from '../events';
|
import { RoomWidgetFloodControlEvent, RoomWidgetUpdateEvent } from '../events';
|
||||||
import { RoomWidgetChatMessage, RoomWidgetChatSelectAvatarMessage, RoomWidgetChatTypingMessage, RoomWidgetMessage, RoomWidgetRequestWidgetMessage } from '../messages';
|
import { RoomWidgetChatMessage, RoomWidgetChatSelectAvatarMessage, RoomWidgetChatTypingMessage, RoomWidgetMessage, RoomWidgetRequestWidgetMessage } from '../messages';
|
||||||
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
||||||
@ -65,6 +66,17 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
|
|||||||
|
|
||||||
switch(firstPart.toLowerCase())
|
switch(firstPart.toLowerCase())
|
||||||
{
|
{
|
||||||
|
case ':shake':
|
||||||
|
RoomShakingEffect.init(2500, 5000);
|
||||||
|
RoomShakingEffect.turnVisualizationOn();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case ':rotate':
|
||||||
|
RoomRotatingEffect.init(2500, 5000);
|
||||||
|
RoomRotatingEffect.turnVisualizationOn();
|
||||||
|
|
||||||
|
return null;
|
||||||
case ':d':
|
case ':d':
|
||||||
case ';d':
|
case ';d':
|
||||||
if(GetSessionDataManager().clubLevel === HabboClubLevelEnum.VIP)
|
if(GetSessionDataManager().clubLevel === HabboClubLevelEnum.VIP)
|
||||||
@ -109,6 +121,7 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
case ':iddqd':
|
case ':iddqd':
|
||||||
|
case ':flip':
|
||||||
GetRoomEngine().events.dispatchEvent(new RoomZoomEvent(this.container.roomSession.roomId, -1, true));
|
GetRoomEngine().events.dispatchEvent(new RoomZoomEvent(this.container.roomSession.roomId, -1, true));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -127,10 +140,11 @@ export class RoomWidgetChatInputHandler extends RoomWidgetHandler
|
|||||||
newWindow.document.write(image.outerHTML);
|
newWindow.document.write(image.outerHTML);
|
||||||
return null;
|
return null;
|
||||||
case ':pickall':
|
case ':pickall':
|
||||||
// this.container.notificationService.alertWithConfirm('${room.confirm.pick_all}', '${generic.alert.title}', () =>
|
NotificationUtilities.confirm(LocalizeText('room.confirm.pick_all'), () =>
|
||||||
// {
|
{
|
||||||
// GetSessionDataManager().sendSpecialCommandMessage(':pickall');
|
GetSessionDataManager().sendSpecialCommandMessage(':pickall');
|
||||||
// });
|
},
|
||||||
|
null, null, null, LocalizeText('generic.alert.title'));
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
case ':furni':
|
case ':furni':
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer';
|
import { IFurnitureData, NitroEvent, ObjectDataFactory, PetFigureData, PetRespectComposer, PetSupplementComposer, PetType, RoomControllerLevel, RoomModerationSettings, RoomObjectCategory, RoomObjectOperationType, RoomObjectType, RoomObjectVariable, RoomSessionPetInfoUpdateEvent, RoomSessionUserBadgesEvent, RoomSessionUserFigureUpdateEvent, RoomTradingLevelEnum, RoomUnitDropHandItemComposer, RoomUnitGiveHandItemComposer, RoomUnitGiveHandItemPetComposer, RoomUserData, RoomWidgetEnum, RoomWidgetEnumItemExtradataParameter, Vector3d } from '@nitrots/nitro-renderer';
|
||||||
import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..';
|
import { GetNitroInstance, GetRoomEngine, GetSessionDataManager, IsOwnerOfFurniture } from '../../../..';
|
||||||
import { InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../../../events';
|
import { InventoryTradeRequestEvent, WiredSelectObjectEvent } from '../../../../../events';
|
||||||
import { FriendsSendFriendRequestEvent } from '../../../../../events/friends/FriendsSendFriendRequestEvent';
|
import { FriendsSendFriendRequestEvent } from '../../../../../events/friends/FriendsSendFriendRequestEvent';
|
||||||
@ -24,6 +24,9 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
|
|||||||
case RoomSessionUserBadgesEvent.RSUBE_BADGES:
|
case RoomSessionUserBadgesEvent.RSUBE_BADGES:
|
||||||
this.container.eventDispatcher.dispatchEvent(event);
|
this.container.eventDispatcher.dispatchEvent(event);
|
||||||
return;
|
return;
|
||||||
|
case RoomSessionUserFigureUpdateEvent.USER_FIGURE:
|
||||||
|
this.processRoomSessionUserFigureUpdateEvent((event as RoomSessionUserFigureUpdateEvent));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,6 +664,17 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
|
|||||||
this.container.eventDispatcher.dispatchEvent(infostandEvent);
|
this.container.eventDispatcher.dispatchEvent(infostandEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private processRoomSessionUserFigureUpdateEvent(event: RoomSessionUserFigureUpdateEvent): void
|
||||||
|
{
|
||||||
|
const userData = this.container.roomSession.userDataManager.getUserDataByIndex(event.userId);
|
||||||
|
|
||||||
|
if(!userData) return;
|
||||||
|
|
||||||
|
// update active infostand figure
|
||||||
|
// update motto
|
||||||
|
// update activity points
|
||||||
|
}
|
||||||
|
|
||||||
private checkGuildSetting(event: RoomWidgetUpdateInfostandUserEvent): boolean
|
private checkGuildSetting(event: RoomWidgetUpdateInfostandUserEvent): boolean
|
||||||
{
|
{
|
||||||
if(event.isGuildRoom) return (event.roomControllerLevel >= RoomControllerLevel.GUILD_ADMIN);
|
if(event.isGuildRoom) return (event.roomControllerLevel >= RoomControllerLevel.GUILD_ADMIN);
|
||||||
@ -766,7 +780,8 @@ export class RoomWidgetInfostandHandler extends RoomWidgetHandler
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
RoomSessionPetInfoUpdateEvent.PET_INFO,
|
RoomSessionPetInfoUpdateEvent.PET_INFO,
|
||||||
RoomSessionUserBadgesEvent.RSUBE_BADGES
|
RoomSessionUserBadgesEvent.RSUBE_BADGES,
|
||||||
|
RoomSessionUserFigureUpdateEvent.USER_FIGURE
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { IFurnitureData } from '@nitrots/nitro-renderer';
|
import { IFurnitureData } from '@nitrots/nitro-renderer';
|
||||||
import { GetSessionDataManager } from '.';
|
import { GetSessionDataManager } from '.';
|
||||||
import { ProductTypeEnum } from '../../../views/catalog/common/ProductTypeEnum';
|
import { ProductTypeEnum } from '../../../components/catalog/common/ProductTypeEnum';
|
||||||
|
|
||||||
export function GetFurnitureData(furniClassId: number, productType: string): IFurnitureData
|
export function GetFurnitureData(furniClassId: number, productType: string): IFurnitureData
|
||||||
{
|
{
|
||||||
let furniData: IFurnitureData = null;
|
let furniData: IFurnitureData = null;
|
||||||
|
|
||||||
switch(productType.toUpperCase())
|
switch(productType.toLowerCase())
|
||||||
{
|
{
|
||||||
case ProductTypeEnum.FLOOR:
|
case ProductTypeEnum.FLOOR:
|
||||||
furniData = GetSessionDataManager().getFloorItemData(furniClassId);
|
furniData = GetSessionDataManager().getFloorItemData(furniClassId);
|
||||||
|
16
src/api/nitro/session/IsOwnerOfFloorFurniture.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { RoomObjectCategory, RoomObjectVariable } from '@nitrots/nitro-renderer';
|
||||||
|
import { GetRoomSession } from '.';
|
||||||
|
import { GetRoomEngine } from '..';
|
||||||
|
import { GetSessionDataManager } from '../../../api';
|
||||||
|
|
||||||
|
export function IsOwnerOfFloorFurniture(id: number): boolean
|
||||||
|
{
|
||||||
|
const roomObject = GetRoomEngine().getRoomObject(GetRoomSession().roomId, id, RoomObjectCategory.FLOOR);
|
||||||
|
|
||||||
|
if(!roomObject || !roomObject.model) return false;
|
||||||
|
|
||||||
|
const userId = GetSessionDataManager().userId;
|
||||||
|
const objectOwnerId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_OWNER_ID);
|
||||||
|
|
||||||
|
return (userId === objectOwnerId);
|
||||||
|
}
|
@ -14,6 +14,7 @@ export * from './GetSessionDataManager';
|
|||||||
export * from './GoToDesktop';
|
export * from './GoToDesktop';
|
||||||
export * from './HasHabboClub';
|
export * from './HasHabboClub';
|
||||||
export * from './HasHabboVip';
|
export * from './HasHabboVip';
|
||||||
|
export * from './IsOwnerOfFloorFurniture';
|
||||||
export * from './IsOwnerOfFurniture';
|
export * from './IsOwnerOfFurniture';
|
||||||
export * from './IsRidingHorse';
|
export * from './IsRidingHorse';
|
||||||
export * from './StartRoomSession';
|
export * from './StartRoomSession';
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/campaign/available.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/assets/images/campaign/campaign_day_generic_bg.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/assets/images/campaign/campaign_opened.png
Normal file
After Width: | Height: | Size: 744 B |
BIN
src/assets/images/campaign/locked.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
src/assets/images/campaign/locked_bg.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
src/assets/images/campaign/next.png
Normal file
After Width: | Height: | Size: 244 B |
BIN
src/assets/images/campaign/prev.png
Normal file
After Width: | Height: | Size: 235 B |
BIN
src/assets/images/campaign/unavailable.png
Normal file
After Width: | Height: | Size: 448 B |
BIN
src/assets/images/campaign/unlocked_bg.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
src/assets/images/hc-center/benefits.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/hc-center/clock.png
Normal file
After Width: | Height: | Size: 267 B |
BIN
src/assets/images/hc-center/hc_logo.gif
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/images/hc-center/payday.png
Normal file
After Width: | Height: | Size: 721 B |
BIN
src/assets/images/icons/chat-history.png
Normal file
After Width: | Height: | Size: 574 B |
BIN
src/assets/images/icons/cog.png
Normal file
After Width: | Height: | Size: 448 B |
BIN
src/assets/images/icons/like-room.png
Normal file
After Width: | Height: | Size: 481 B |
BIN
src/assets/images/icons/room-link.png
Normal file
After Width: | Height: | Size: 322 B |
BIN
src/assets/images/icons/small-room.png
Normal file
After Width: | Height: | Size: 413 B |
BIN
src/assets/images/icons/zoom-less.png
Normal file
After Width: | Height: | Size: 252 B |
BIN
src/assets/images/icons/zoom-more.png
Normal file
After Width: | Height: | Size: 475 B |
@ -79,6 +79,11 @@
|
|||||||
font-weight: $font-weight-normal;
|
font-weight: $font-weight-normal;
|
||||||
color: $btn-link-color;
|
color: $btn-link-color;
|
||||||
text-decoration: $link-decoration;
|
text-decoration: $link-decoration;
|
||||||
|
box-shadow: none !important;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
color: $btn-link-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $btn-link-hover-color;
|
color: $btn-link-hover-color;
|
||||||
|
@ -4,34 +4,37 @@
|
|||||||
// `<nav>`s, `<ul>`s or `<ol>`s.
|
// `<nav>`s, `<ul>`s or `<ol>`s.
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
display: block;
|
display: block;
|
||||||
padding: $nav-link-padding-y $nav-link-padding-x;
|
padding: $nav-link-padding-y $nav-link-padding-x;
|
||||||
@include font-size($nav-link-font-size);
|
@include font-size($nav-link-font-size);
|
||||||
font-weight: $nav-link-font-weight;
|
font-weight: $nav-link-font-weight;
|
||||||
color: $nav-link-color;
|
color: $nav-link-color;
|
||||||
text-decoration: if($link-decoration == none, null, none);
|
text-decoration: if($link-decoration == none, null, none);
|
||||||
@include transition($nav-link-transition);
|
@include transition($nav-link-transition);
|
||||||
|
|
||||||
&:hover,
|
display: flex;
|
||||||
&:focus {
|
align-items: center;
|
||||||
color: $nav-link-hover-color;
|
|
||||||
text-decoration: if($link-hover-decoration == underline, none, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disabled state lightens text
|
&:hover,
|
||||||
&.disabled {
|
&:focus {
|
||||||
color: $nav-link-disabled-color;
|
color: $nav-link-hover-color;
|
||||||
pointer-events: none;
|
text-decoration: if($link-hover-decoration == underline, none, null);
|
||||||
cursor: default;
|
}
|
||||||
}
|
|
||||||
|
// Disabled state lightens text
|
||||||
|
&.disabled {
|
||||||
|
color: $nav-link-disabled-color;
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -39,122 +42,119 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
|
border-bottom: $nav-tabs-border-width solid $nav-tabs-border-color;
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-bottom: -$nav-tabs-border-width;
|
margin-bottom: -$nav-tabs-border-width;
|
||||||
background-color: $nav-tabs-link-bg;
|
background-color: $nav-tabs-link-bg;
|
||||||
border: $nav-tabs-border-width solid $nav-tabs-border-color;
|
border: $nav-tabs-border-width solid $nav-tabs-border-color;
|
||||||
@include border-top-radius($nav-tabs-border-radius);
|
@include border-top-radius($nav-tabs-border-radius);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
&:before {
|
&:before {
|
||||||
background: #FFFFFF;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 93%;
|
||||||
|
height: 3px;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
top: 1.5px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: auto;
|
||||||
|
background: #c2c9d1;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
border-color: $nav-tabs-link-hover-border-color;
|
||||||
|
// Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
color: $nav-link-disabled-color;
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
.nav-link.active,
|
||||||
content: "";
|
.nav-item.show .nav-link {
|
||||||
position: absolute;
|
color: $nav-tabs-link-active-color;
|
||||||
width: 93%;
|
background-color: $nav-tabs-link-active-bg;
|
||||||
height: 3px;
|
border-color: $nav-tabs-link-active-border-color;
|
||||||
border-radius: $border-radius;
|
|
||||||
top: 1.5px;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
background: #C2C9D1;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
.dropdown-menu {
|
||||||
&:focus {
|
// Make dropdown border overlap tab border
|
||||||
border-color: $nav-tabs-link-hover-border-color;
|
margin-top: -$nav-tabs-border-width;
|
||||||
// Prevents active .nav-link tab overlapping focus outline of previous/next .nav-link
|
// Remove the top rounded corners here since there is a hard edge above the menu
|
||||||
isolation: isolate;
|
@include border-top-radius(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
|
||||||
color: $nav-link-disabled-color;
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link.active,
|
|
||||||
.nav-item.show .nav-link {
|
|
||||||
color: $nav-tabs-link-active-color;
|
|
||||||
background-color: $nav-tabs-link-active-bg;
|
|
||||||
border-color: $nav-tabs-link-active-border-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
// Make dropdown border overlap tab border
|
|
||||||
margin-top: -$nav-tabs-border-width;
|
|
||||||
// Remove the top rounded corners here since there is a hard edge above the menu
|
|
||||||
@include border-top-radius(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pills
|
// Pills
|
||||||
//
|
//
|
||||||
|
|
||||||
.nav-pills {
|
.nav-pills {
|
||||||
.nav-link {
|
.nav-link {
|
||||||
background: none;
|
background: none;
|
||||||
border: 0;
|
border: 0;
|
||||||
@include border-radius($nav-pills-border-radius);
|
@include border-radius($nav-pills-border-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link.active,
|
.nav-link.active,
|
||||||
.show > .nav-link {
|
.show > .nav-link {
|
||||||
color: $nav-pills-link-active-color;
|
color: $nav-pills-link-active-color;
|
||||||
@include gradient-bg($nav-pills-link-active-bg);
|
@include gradient-bg($nav-pills-link-active-bg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Justified variants
|
// Justified variants
|
||||||
//
|
//
|
||||||
|
|
||||||
.nav-fill {
|
.nav-fill {
|
||||||
> .nav-link,
|
> .nav-link,
|
||||||
.nav-item {
|
.nav-item {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-justified {
|
.nav-justified {
|
||||||
> .nav-link,
|
> .nav-link,
|
||||||
.nav-item {
|
.nav-item {
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-fill,
|
.nav-fill,
|
||||||
.nav-justified {
|
.nav-justified {
|
||||||
.nav-item .nav-link {
|
.nav-item .nav-link {
|
||||||
width: 100%; // Make sure button will grow
|
width: 100%; // Make sure button will grow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tabbable tabs
|
// Tabbable tabs
|
||||||
//
|
//
|
||||||
// Hide tabbable panes to start, show them when `.active`
|
// Hide tabbable panes to start, show them when `.active`
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
> .tab-pane {
|
> .tab-pane {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
> .active {
|
> .active {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
|
// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix
|
||||||
|
|
||||||
|
|
||||||
// Reboot
|
// Reboot
|
||||||
//
|
//
|
||||||
// Normalization of HTML elements, manually forked from Normalize.css to remove
|
// Normalization of HTML elements, manually forked from Normalize.css to remove
|
||||||
@ -8,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
|
// Normalize is licensed MIT. https://github.com/necolas/normalize.css
|
||||||
|
|
||||||
|
|
||||||
// Document
|
// Document
|
||||||
//
|
//
|
||||||
// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
|
// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.
|
||||||
@ -16,27 +14,26 @@
|
|||||||
*,
|
*,
|
||||||
*::before,
|
*::before,
|
||||||
*::after {
|
*::after {
|
||||||
box-sizing: border-box;
|
line-height: normal;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Root
|
// Root
|
||||||
//
|
//
|
||||||
// Ability to the value of the root font sizes, affecting the value of `rem`.
|
// Ability to the value of the root font sizes, affecting the value of `rem`.
|
||||||
// null by default, thus nothing is generated.
|
// null by default, thus nothing is generated.
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
@if $font-size-root != null {
|
@if $font-size-root != null {
|
||||||
font-size: var(--#{$variable-prefix}-root-font-size);
|
font-size: var(--#{$variable-prefix}-root-font-size);
|
||||||
}
|
|
||||||
|
|
||||||
@if $enable-smooth-scroll {
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@if $enable-smooth-scroll {
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
//
|
//
|
||||||
@ -47,38 +44,36 @@
|
|||||||
|
|
||||||
// scss-docs-start reboot-body-rules
|
// scss-docs-start reboot-body-rules
|
||||||
body {
|
body {
|
||||||
margin: 0; // 1
|
margin: 0; // 1
|
||||||
font-family: var(--#{$variable-prefix}body-font-family);
|
font-family: var(--#{$variable-prefix}body-font-family);
|
||||||
@include font-size(var(--#{$variable-prefix}body-font-size));
|
@include font-size(var(--#{$variable-prefix}body-font-size));
|
||||||
font-weight: var(--#{$variable-prefix}body-font-weight);
|
font-weight: var(--#{$variable-prefix}body-font-weight);
|
||||||
line-height: var(--#{$variable-prefix}body-line-height);
|
line-height: var(--#{$variable-prefix}body-line-height);
|
||||||
color: var(--#{$variable-prefix}body-color);
|
color: var(--#{$variable-prefix}body-color);
|
||||||
text-align: var(--#{$variable-prefix}body-text-align);
|
text-align: var(--#{$variable-prefix}body-text-align);
|
||||||
background-color: var(--#{$variable-prefix}body-bg); // 2
|
background-color: var(--#{$variable-prefix}body-bg); // 2
|
||||||
-webkit-text-size-adjust: 100%; // 3
|
-webkit-text-size-adjust: 100%; // 3
|
||||||
-webkit-tap-highlight-color: rgba($black, 0); // 4
|
-webkit-tap-highlight-color: rgba($black, 0); // 4
|
||||||
}
|
}
|
||||||
// scss-docs-end reboot-body-rules
|
// scss-docs-end reboot-body-rules
|
||||||
|
|
||||||
|
|
||||||
// Content grouping
|
// Content grouping
|
||||||
//
|
//
|
||||||
// 1. Reset Firefox's gray color
|
// 1. Reset Firefox's gray color
|
||||||
// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field
|
// 2. Set correct height and prevent the `size` attribute to make the `hr` look like an input field
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: $hr-margin-y 0;
|
margin: $hr-margin-y 0;
|
||||||
color: $hr-color; // 1
|
color: $hr-color; // 1
|
||||||
background-color: currentColor;
|
background-color: currentColor;
|
||||||
border: 0;
|
border: 0;
|
||||||
opacity: $hr-opacity;
|
opacity: $hr-opacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr:not([size]) {
|
hr:not([size]) {
|
||||||
height: $hr-height; // 2
|
height: $hr-height; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Typography
|
// Typography
|
||||||
//
|
//
|
||||||
// 1. Remove top margins from headings
|
// 1. Remove top margins from headings
|
||||||
@ -86,57 +81,55 @@ hr:not([size]) {
|
|||||||
// margin for easier control within type scales as it avoids margin collapsing.
|
// margin for easier control within type scales as it avoids margin collapsing.
|
||||||
|
|
||||||
%heading {
|
%heading {
|
||||||
margin-top: 0; // 1
|
margin-top: 0; // 1
|
||||||
margin-bottom: $headings-margin-bottom;
|
margin-bottom: $headings-margin-bottom;
|
||||||
font-family: $headings-font-family;
|
font-family: $headings-font-family;
|
||||||
font-style: $headings-font-style;
|
font-style: $headings-font-style;
|
||||||
font-weight: $headings-font-weight;
|
font-weight: $headings-font-weight;
|
||||||
line-height: $headings-line-height;
|
line-height: $headings-line-height;
|
||||||
color: $headings-color;
|
color: $headings-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h1-font-size);
|
@include font-size($h1-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h2-font-size);
|
@include font-size($h2-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h3-font-size);
|
@include font-size($h3-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
h4 {
|
h4 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h4-font-size);
|
@include font-size($h4-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h5-font-size);
|
@include font-size($h5-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
h6 {
|
h6 {
|
||||||
@extend %heading;
|
@extend %heading;
|
||||||
@include font-size($h6-font-size);
|
@include font-size($h6-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Reset margins on paragraphs
|
// Reset margins on paragraphs
|
||||||
//
|
//
|
||||||
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
|
// Similarly, the top margin on `<p>`s get reset. However, we also reset the
|
||||||
// bottom margin to use `rem` units instead of `em`.
|
// bottom margin to use `rem` units instead of `em`.
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: $paragraph-margin-bottom;
|
margin-bottom: $paragraph-margin-bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Abbreviations
|
// Abbreviations
|
||||||
//
|
//
|
||||||
// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin
|
// 1. Duplicate behavior to the data-bs-* attribute for our tooltip plugin
|
||||||
@ -145,89 +138,83 @@ p {
|
|||||||
// 4. Prevent the text-decoration to be skipped.
|
// 4. Prevent the text-decoration to be skipped.
|
||||||
|
|
||||||
abbr[title],
|
abbr[title],
|
||||||
abbr[data-bs-original-title] { // 1
|
abbr[data-bs-original-title] {
|
||||||
text-decoration: underline dotted; // 2
|
// 1
|
||||||
cursor: help; // 3
|
text-decoration: underline dotted; // 2
|
||||||
text-decoration-skip-ink: none; // 4
|
cursor: help; // 3
|
||||||
|
text-decoration-skip-ink: none; // 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Address
|
// Address
|
||||||
|
|
||||||
address {
|
address {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Lists
|
// Lists
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
ul {
|
ul {
|
||||||
padding-left: 2rem;
|
padding-left: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
ul,
|
ul,
|
||||||
dl {
|
dl {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
ol ol,
|
ol ol,
|
||||||
ul ul,
|
ul ul,
|
||||||
ol ul,
|
ol ul,
|
||||||
ul ol {
|
ul ol {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dt {
|
dt {
|
||||||
font-weight: $dt-font-weight;
|
font-weight: $dt-font-weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Undo browser default
|
// 1. Undo browser default
|
||||||
|
|
||||||
dd {
|
dd {
|
||||||
margin-bottom: .5rem;
|
margin-bottom: 0.5rem;
|
||||||
margin-left: 0; // 1
|
margin-left: 0; // 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Blockquote
|
// Blockquote
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
margin: 0 0 1rem;
|
margin: 0 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Strong
|
// Strong
|
||||||
//
|
//
|
||||||
// Add the correct font weight in Chrome, Edge, and Safari
|
// Add the correct font weight in Chrome, Edge, and Safari
|
||||||
|
|
||||||
b,
|
b,
|
||||||
strong {
|
strong {
|
||||||
font-weight: $font-weight-bolder;
|
font-weight: $font-weight-bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Small
|
// Small
|
||||||
//
|
//
|
||||||
// Add the correct font size in all browsers
|
// Add the correct font size in all browsers
|
||||||
|
|
||||||
small {
|
small {
|
||||||
@include font-size($small-font-size);
|
@include font-size($small-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Mark
|
// Mark
|
||||||
|
|
||||||
mark {
|
mark {
|
||||||
padding: $mark-padding;
|
padding: $mark-padding;
|
||||||
background-color: $mark-bg;
|
background-color: $mark-bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sub and Sup
|
// Sub and Sup
|
||||||
//
|
//
|
||||||
// Prevent `sub` and `sup` elements from affecting the line height in
|
// Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
@ -235,26 +222,29 @@ mark {
|
|||||||
|
|
||||||
sub,
|
sub,
|
||||||
sup {
|
sup {
|
||||||
position: relative;
|
position: relative;
|
||||||
@include font-size($sub-sup-font-size);
|
@include font-size($sub-sup-font-size);
|
||||||
line-height: 0;
|
line-height: 0;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub { bottom: -.25em; }
|
sub {
|
||||||
sup { top: -.5em; }
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: $link-color;
|
color: $link-color;
|
||||||
text-decoration: $link-decoration;
|
text-decoration: $link-decoration;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $link-hover-color;
|
color: $link-hover-color;
|
||||||
text-decoration: $link-hover-decoration;
|
text-decoration: $link-hover-decoration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// And undo these styles for placeholder links/named anchors (without href).
|
// And undo these styles for placeholder links/named anchors (without href).
|
||||||
@ -263,24 +253,25 @@ a {
|
|||||||
// See https://github.com/twbs/bootstrap/issues/19402
|
// See https://github.com/twbs/bootstrap/issues/19402
|
||||||
|
|
||||||
a:not([href]):not([class]) {
|
a:not([href]):not([class]) {
|
||||||
&,
|
&,
|
||||||
&:hover {
|
&:hover {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Code
|
// Code
|
||||||
|
|
||||||
pre,
|
pre,
|
||||||
code,
|
code,
|
||||||
kbd,
|
kbd,
|
||||||
samp {
|
samp {
|
||||||
font-family: $font-family-code;
|
font-family: $font-family-code;
|
||||||
@include font-size(1em); // Correct the odd `em` font sizing in all browsers.
|
@include font-size(
|
||||||
direction: ltr #{"/* rtl:ignore */"};
|
1em
|
||||||
unicode-bidi: bidi-override;
|
); // Correct the odd `em` font sizing in all browsers.
|
||||||
|
direction: ltr #{"/* rtl:ignore */"};
|
||||||
|
unicode-bidi: bidi-override;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Remove browser default top margin
|
// 1. Remove browser default top margin
|
||||||
@ -288,78 +279,75 @@ samp {
|
|||||||
// 3. Don't allow content to break outside
|
// 3. Don't allow content to break outside
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 0; // 1
|
margin-top: 0; // 1
|
||||||
margin-bottom: 1rem; // 2
|
margin-bottom: 1rem; // 2
|
||||||
overflow: auto; // 3
|
overflow: auto; // 3
|
||||||
@include font-size($code-font-size);
|
@include font-size($code-font-size);
|
||||||
color: $pre-color;
|
color: $pre-color;
|
||||||
|
|
||||||
// Account for some code outputs that place code tags in pre tags
|
// Account for some code outputs that place code tags in pre tags
|
||||||
code {
|
code {
|
||||||
@include font-size(inherit);
|
@include font-size(inherit);
|
||||||
color: inherit;
|
color: inherit;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
@include font-size($code-font-size);
|
@include font-size($code-font-size);
|
||||||
color: $code-color;
|
color: $code-color;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
||||||
// Streamline the style when inside anchors to avoid broken underline and more
|
// Streamline the style when inside anchors to avoid broken underline and more
|
||||||
a > & {
|
a > & {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
padding: $kbd-padding-y $kbd-padding-x;
|
padding: $kbd-padding-y $kbd-padding-x;
|
||||||
@include font-size($kbd-font-size);
|
@include font-size($kbd-font-size);
|
||||||
color: $kbd-color;
|
color: $kbd-color;
|
||||||
background-color: $kbd-bg;
|
background-color: $kbd-bg;
|
||||||
@include border-radius($border-radius-sm);
|
@include border-radius($border-radius-sm);
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@include font-size(1em);
|
@include font-size(1em);
|
||||||
font-weight: $nested-kbd-font-weight;
|
font-weight: $nested-kbd-font-weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Figures
|
// Figures
|
||||||
//
|
//
|
||||||
// Apply a consistent margin strategy (matches our type styles).
|
// Apply a consistent margin strategy (matches our type styles).
|
||||||
|
|
||||||
figure {
|
figure {
|
||||||
margin: 0 0 1rem;
|
margin: 0 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Images and content
|
// Images and content
|
||||||
|
|
||||||
img,
|
img,
|
||||||
svg {
|
svg {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Tables
|
// Tables
|
||||||
//
|
//
|
||||||
// Prevent double borders
|
// Prevent double borders
|
||||||
|
|
||||||
table {
|
table {
|
||||||
caption-side: bottom;
|
caption-side: bottom;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
caption {
|
caption {
|
||||||
padding-top: $table-cell-padding-y;
|
padding-top: $table-cell-padding-y;
|
||||||
padding-bottom: $table-cell-padding-y;
|
padding-bottom: $table-cell-padding-y;
|
||||||
color: $table-caption-color;
|
color: $table-caption-color;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Removes font-weight bold by inheriting
|
// 1. Removes font-weight bold by inheriting
|
||||||
@ -367,9 +355,9 @@ caption {
|
|||||||
// 3. Fix alignment for Safari
|
// 3. Fix alignment for Safari
|
||||||
|
|
||||||
th {
|
th {
|
||||||
font-weight: $table-th-font-weight; // 1
|
font-weight: $table-th-font-weight; // 1
|
||||||
text-align: inherit; // 2
|
text-align: inherit; // 2
|
||||||
text-align: -webkit-match-parent; // 3
|
text-align: -webkit-match-parent; // 3
|
||||||
}
|
}
|
||||||
|
|
||||||
thead,
|
thead,
|
||||||
@ -378,26 +366,25 @@ tfoot,
|
|||||||
tr,
|
tr,
|
||||||
td,
|
td,
|
||||||
th {
|
th {
|
||||||
border-color: inherit;
|
border-color: inherit;
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Forms
|
// Forms
|
||||||
//
|
//
|
||||||
// 1. Allow labels to use `margin` for spacing.
|
// 1. Allow labels to use `margin` for spacing.
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: inline-block; // 1
|
display: inline-block; // 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the default `border-radius` that macOS Chrome adds.
|
// Remove the default `border-radius` that macOS Chrome adds.
|
||||||
// See https://github.com/twbs/bootstrap/issues/24093
|
// See https://github.com/twbs/bootstrap/issues/24093
|
||||||
|
|
||||||
button {
|
button {
|
||||||
// stylelint-disable-next-line property-disallowed-list
|
// stylelint-disable-next-line property-disallowed-list
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicitly remove focus outline in Chromium when it shouldn't be
|
// Explicitly remove focus outline in Chromium when it shouldn't be
|
||||||
@ -406,7 +393,7 @@ button {
|
|||||||
// confused and applies its very visible two-tone outline anyway.
|
// confused and applies its very visible two-tone outline anyway.
|
||||||
|
|
||||||
button:focus:not(:focus-visible) {
|
button:focus:not(:focus-visible) {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Remove the margin in Firefox and Safari
|
// 1. Remove the margin in Firefox and Safari
|
||||||
@ -416,40 +403,40 @@ button,
|
|||||||
select,
|
select,
|
||||||
optgroup,
|
optgroup,
|
||||||
textarea {
|
textarea {
|
||||||
margin: 0; // 1
|
margin: 0; // 1
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
@include font-size(inherit);
|
@include font-size(inherit);
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the inheritance of text transform in Firefox
|
// Remove the inheritance of text transform in Firefox
|
||||||
button,
|
button,
|
||||||
select {
|
select {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
// Set the cursor for non-`<button>` buttons
|
// Set the cursor for non-`<button>` buttons
|
||||||
//
|
//
|
||||||
// Details at https://github.com/twbs/bootstrap/pull/30562
|
// Details at https://github.com/twbs/bootstrap/pull/30562
|
||||||
[role="button"] {
|
[role="button"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
// Remove the inheritance of word-wrap in Safari.
|
// Remove the inheritance of word-wrap in Safari.
|
||||||
// See https://github.com/twbs/bootstrap/issues/24990
|
// See https://github.com/twbs/bootstrap/issues/24990
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
|
|
||||||
// Undo the opacity change from Chrome
|
// Undo the opacity change from Chrome
|
||||||
&:disabled {
|
&:disabled {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the dropdown arrow in Chrome from inputs built with datalists.
|
// Remove the dropdown arrow in Chrome from inputs built with datalists.
|
||||||
// See https://stackoverflow.com/a/54997118
|
// See https://stackoverflow.com/a/54997118
|
||||||
|
|
||||||
[list]::-webkit-calendar-picker-indicator {
|
[list]::-webkit-calendar-picker-indicator {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
@ -461,26 +448,26 @@ button,
|
|||||||
[type="button"], // 1
|
[type="button"], // 1
|
||||||
[type="reset"],
|
[type="reset"],
|
||||||
[type="submit"] {
|
[type="submit"] {
|
||||||
-webkit-appearance: button; // 2
|
-webkit-appearance: button; // 2
|
||||||
|
|
||||||
@if $enable-button-pointers {
|
@if $enable-button-pointers {
|
||||||
&:not(:disabled) {
|
&:not(:disabled) {
|
||||||
cursor: pointer; // 3
|
cursor: pointer; // 3
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
|
// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.
|
||||||
|
|
||||||
::-moz-focus-inner {
|
::-moz-focus-inner {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-style: none;
|
border-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Textareas should really only resize vertically so they don't break their (horizontal) containers.
|
// 1. Textareas should really only resize vertically so they don't break their (horizontal) containers.
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
resize: vertical; // 1
|
resize: vertical; // 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Browsers set a default `min-width: min-content;` on fieldsets,
|
// 1. Browsers set a default `min-width: min-content;` on fieldsets,
|
||||||
@ -491,10 +478,10 @@ textarea {
|
|||||||
// 2. Reset the default outline behavior of fieldsets so they don't affect page layout.
|
// 2. Reset the default outline behavior of fieldsets so they don't affect page layout.
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
min-width: 0; // 1
|
min-width: 0; // 1
|
||||||
padding: 0; // 2
|
padding: 0; // 2
|
||||||
margin: 0; // 2
|
margin: 0; // 2
|
||||||
border: 0; // 2
|
border: 0; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. By using `float: left`, the legend will behave like a block element.
|
// 1. By using `float: left`, the legend will behave like a block element.
|
||||||
@ -503,17 +490,17 @@ fieldset {
|
|||||||
// See https://github.com/twbs/bootstrap/issues/29712
|
// See https://github.com/twbs/bootstrap/issues/29712
|
||||||
|
|
||||||
legend {
|
legend {
|
||||||
float: left; // 1
|
float: left; // 1
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: $legend-margin-bottom;
|
margin-bottom: $legend-margin-bottom;
|
||||||
@include font-size($legend-font-size);
|
@include font-size($legend-font-size);
|
||||||
font-weight: $legend-font-weight;
|
font-weight: $legend-font-weight;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
|
|
||||||
+ * {
|
+ * {
|
||||||
clear: left; // 2
|
clear: left; // 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix height of inputs with a type of datetime-local, date, month, week, or time
|
// Fix height of inputs with a type of datetime-local, date, month, week, or time
|
||||||
@ -526,11 +513,11 @@ legend {
|
|||||||
::-webkit-datetime-edit-day-field,
|
::-webkit-datetime-edit-day-field,
|
||||||
::-webkit-datetime-edit-month-field,
|
::-webkit-datetime-edit-month-field,
|
||||||
::-webkit-datetime-edit-year-field {
|
::-webkit-datetime-edit-year-field {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-inner-spin-button {
|
::-webkit-inner-spin-button {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Correct the outline style in Safari.
|
// 1. Correct the outline style in Safari.
|
||||||
@ -540,8 +527,8 @@ legend {
|
|||||||
// https://github.com/twbs/bootstrap/issues/11586.
|
// https://github.com/twbs/bootstrap/issues/11586.
|
||||||
|
|
||||||
[type="search"] {
|
[type="search"] {
|
||||||
outline-offset: -2px; // 1
|
outline-offset: -2px; // 1
|
||||||
-webkit-appearance: textfield; // 2
|
-webkit-appearance: textfield; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. A few input types should stay LTR
|
// 1. A few input types should stay LTR
|
||||||
@ -561,40 +548,39 @@ legend {
|
|||||||
// Remove the inner padding in Chrome and Safari on macOS.
|
// Remove the inner padding in Chrome and Safari on macOS.
|
||||||
|
|
||||||
::-webkit-search-decoration {
|
::-webkit-search-decoration {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove padding around color pickers in webkit browsers
|
// Remove padding around color pickers in webkit browsers
|
||||||
|
|
||||||
::-webkit-color-swatch-wrapper {
|
::-webkit-color-swatch-wrapper {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Inherit font family and line height for file input buttons
|
// Inherit font family and line height for file input buttons
|
||||||
|
|
||||||
::file-selector-button {
|
::file-selector-button {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Change font properties to `inherit`
|
// 1. Change font properties to `inherit`
|
||||||
// 2. Correct the inability to style clickable types in iOS and Safari.
|
// 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
|
||||||
::-webkit-file-upload-button {
|
::-webkit-file-upload-button {
|
||||||
font: inherit; // 1
|
font: inherit; // 1
|
||||||
-webkit-appearance: button; // 2
|
-webkit-appearance: button; // 2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct element displays
|
// Correct element displays
|
||||||
|
|
||||||
output {
|
output {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove border from iframe
|
// Remove border from iframe
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Summary
|
// Summary
|
||||||
@ -602,24 +588,22 @@ iframe {
|
|||||||
// 1. Add the correct display in all browsers
|
// 1. Add the correct display in all browsers
|
||||||
|
|
||||||
summary {
|
summary {
|
||||||
display: list-item; // 1
|
display: list-item; // 1
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Progress
|
// Progress
|
||||||
//
|
//
|
||||||
// Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
// Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
|
||||||
progress {
|
progress {
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Hidden attribute
|
// Hidden attribute
|
||||||
//
|
//
|
||||||
// Always hide an element with the `hidden` HTML attribute.
|
// Always hide an element with the `hidden` HTML attribute.
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ $enable-cssgrid: true !default;
|
|||||||
$enable-button-pointers: true !default;
|
$enable-button-pointers: true !default;
|
||||||
$enable-rfs: true !default;
|
$enable-rfs: true !default;
|
||||||
$enable-validation-icons: true !default;
|
$enable-validation-icons: true !default;
|
||||||
$enable-negative-margins: false !default;
|
$enable-negative-margins: true !default;
|
||||||
$enable-deprecation-messages: true !default;
|
$enable-deprecation-messages: true !default;
|
||||||
$enable-important-utilities: true !default;
|
$enable-important-utilities: true !default;
|
||||||
|
|
||||||
@ -732,7 +732,7 @@ $table-cell-padding-x-sm: .25rem !default;
|
|||||||
|
|
||||||
$table-cell-vertical-align: top !default;
|
$table-cell-vertical-align: top !default;
|
||||||
|
|
||||||
$table-color: $body-color !default;
|
$table-color: $black !default;
|
||||||
$table-bg: transparent !default;
|
$table-bg: transparent !default;
|
||||||
$table-accent-bg: transparent !default;
|
$table-accent-bg: transparent !default;
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
.form-check-input {
|
.form-check-input {
|
||||||
width: $form-check-input-width;
|
width: $form-check-input-width;
|
||||||
height: $form-check-input-width;
|
height: $form-check-input-width;
|
||||||
margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
|
//margin-top: ($line-height-base - $form-check-input-width) * .5; // line-height minus check height
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
background-color: $form-check-input-bg;
|
background-color: $form-check-input-bg;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.fas {
|
.fas,
|
||||||
|
.svg-inline--fa {
|
||||||
line-height: 0 !important;
|
line-height: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,6 +304,48 @@
|
|||||||
height: 15px;
|
height: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.icon-small-room {
|
||||||
|
background: url("../images/icons/small-room.png");
|
||||||
|
width: 17px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-cog {
|
||||||
|
background: url("../images/icons/cog.png");
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-chat-history {
|
||||||
|
background: url("../images/icons/chat-history.png");
|
||||||
|
width: 17px;
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-room-link {
|
||||||
|
background: url("../images/icons/room-link.png");
|
||||||
|
width: 17px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-zoom-more {
|
||||||
|
background: url("../images/icons/zoom-more.png");
|
||||||
|
width: 12px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-zoom-less {
|
||||||
|
background: url("../images/icons/zoom-less.png");
|
||||||
|
width: 12px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.icon-like-room {
|
||||||
|
background: url("../images/icons/like-room.png");
|
||||||
|
width: 20px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
&.icon-arrows {
|
&.icon-arrows {
|
||||||
background: url("../images/icons/arrows.png");
|
background: url("../images/icons/arrows.png");
|
||||||
width: 17px;
|
width: 17px;
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
@import './fonts';
|
@import './fonts';
|
||||||
@import './bootstrap/bootstrap';
|
@import './bootstrap/bootstrap';
|
||||||
@import './fontawesome/fontawesome';
|
|
||||||
@import './fontawesome/solid';
|
|
||||||
@import './fontawesome/brands';
|
|
||||||
@import './fontawesome/regular';
|
|
||||||
@import '../node_modules/animate.css/animate.min.css';
|
@import '../node_modules/animate.css/animate.min.css';
|
||||||
@import './scrollbars';
|
@import './scrollbars';
|
||||||
@import './slider';
|
@import './slider';
|
||||||
|
@ -47,6 +47,10 @@ ul {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-not-allowed {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.pointer-events-none {
|
.pointer-events-none {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@ -78,3 +82,18 @@ ul {
|
|||||||
.z-index-1 {
|
.z-index-1 {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flex-basis-fit-content {
|
||||||
|
flex-basis: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-basis-max-content {
|
||||||
|
flex-basis: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.striped-children {
|
||||||
|
|
||||||
|
> :nth-child(1) {
|
||||||
|
background-color: $table-striped-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
28
src/common/AutoGrid.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { CSSProperties, FC, useMemo } from 'react';
|
||||||
|
import { Grid, GridProps } from './Grid';
|
||||||
|
|
||||||
|
export interface AutoGridProps extends GridProps
|
||||||
|
{
|
||||||
|
columnMinWidth?: number;
|
||||||
|
columnMinHeight?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AutoGrid: FC<AutoGridProps> = props =>
|
||||||
|
{
|
||||||
|
const { columnMinWidth = 40, columnMinHeight = 40, columnCount = 0, fullHeight = false, maxContent = true, overflow = 'auto', style = {}, ...rest } = props;
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newStyle: CSSProperties = {};
|
||||||
|
|
||||||
|
newStyle['--nitro-grid-column-min-height'] = (columnMinHeight + 'px');
|
||||||
|
|
||||||
|
if(columnCount > 1) newStyle.gridTemplateColumns = `repeat(auto-fill, minmax(${ columnMinWidth }px, 1fr))`;
|
||||||
|
|
||||||
|
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ columnMinWidth, columnMinHeight, columnCount, style ]);
|
||||||
|
|
||||||
|
return <Grid columnCount={ columnCount } fullHeight={ fullHeight } overflow={ overflow } style={ getStyle } { ...rest } />;
|
||||||
|
}
|
70
src/common/Base.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { CSSProperties, DetailedHTMLProps, FC, HTMLAttributes, LegacyRef, useMemo } from 'react';
|
||||||
|
import { ColorVariantType, FloatType, OverflowType, PositionType } from './types';
|
||||||
|
|
||||||
|
export interface BaseProps<T = HTMLElement> extends DetailedHTMLProps<HTMLAttributes<T>, T>
|
||||||
|
{
|
||||||
|
innerRef?: LegacyRef<T>;
|
||||||
|
fit?: boolean;
|
||||||
|
grow?: boolean;
|
||||||
|
shrink?: boolean;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
fullHeight?: boolean;
|
||||||
|
overflow?: OverflowType;
|
||||||
|
position?: PositionType;
|
||||||
|
float?: FloatType;
|
||||||
|
pointer?: boolean;
|
||||||
|
textColor?: ColorVariantType;
|
||||||
|
classNames?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Base: FC<BaseProps<HTMLDivElement>> = props =>
|
||||||
|
{
|
||||||
|
const { ref = null, innerRef = null, fit = false, grow = false, shrink = false, fullWidth = false, fullHeight = false, overflow = null, position = null, float = null, pointer = false, textColor = null, classNames = [], className = '', style = {}, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [];
|
||||||
|
|
||||||
|
if(fit || fullWidth) newClassNames.push('w-100');
|
||||||
|
|
||||||
|
if(fit || fullHeight) newClassNames.push('h-100');
|
||||||
|
|
||||||
|
if(grow) newClassNames.push('flex-grow-1');
|
||||||
|
|
||||||
|
if(shrink) newClassNames.push('flex-shrink-0');
|
||||||
|
|
||||||
|
if(overflow) newClassNames.push('overflow-' + overflow);
|
||||||
|
|
||||||
|
if(position) newClassNames.push('position-' + position);
|
||||||
|
|
||||||
|
if(float) newClassNames.push('float-' + float);
|
||||||
|
|
||||||
|
if(pointer) newClassNames.push('cursor-pointer');
|
||||||
|
|
||||||
|
if(textColor) newClassNames.push('text-' + textColor);
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ fit, grow, shrink, fullWidth, fullHeight, overflow, position, float, pointer, textColor, classNames ]);
|
||||||
|
|
||||||
|
const getClassName = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newClassName = getClassNames.join(' ');
|
||||||
|
|
||||||
|
if(className.length) newClassName += (' ' + className);
|
||||||
|
|
||||||
|
return newClassName.trim();
|
||||||
|
}, [ getClassNames, className ]);
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newStyle: CSSProperties = {};
|
||||||
|
|
||||||
|
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ style ]);
|
||||||
|
|
||||||
|
return <div ref={ innerRef } className={ getClassName } style={ getStyle } { ...rest } />;
|
||||||
|
}
|
35
src/common/Button.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Flex, FlexProps } from './Flex';
|
||||||
|
import { ButtonSizeType, ColorVariantType } from './types';
|
||||||
|
|
||||||
|
export interface ButtonProps extends FlexProps
|
||||||
|
{
|
||||||
|
variant?: ColorVariantType;
|
||||||
|
size?: ButtonSizeType;
|
||||||
|
active?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Button: FC<ButtonProps> = props =>
|
||||||
|
{
|
||||||
|
const { variant = 'primary', size = 'sm', active = false, disabled = false, classNames = [], ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'btn' ];
|
||||||
|
|
||||||
|
if(variant) newClassNames.push('btn-' + variant);
|
||||||
|
|
||||||
|
if(size) newClassNames.push('btn-' + size);
|
||||||
|
|
||||||
|
if(active) newClassNames.push('active');
|
||||||
|
|
||||||
|
if(disabled) newClassNames.push('disabled');
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ variant, size, active, disabled, classNames ]);
|
||||||
|
|
||||||
|
return <Flex center classNames={ getClassNames } { ...rest } />;
|
||||||
|
}
|
22
src/common/ButtonGroup.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Base, BaseProps } from './Base';
|
||||||
|
|
||||||
|
export interface ButtonGroupProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ButtonGroup: FC<ButtonGroupProps> = props =>
|
||||||
|
{
|
||||||
|
const { classNames = [], ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'btn-group' ];
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ classNames ]);
|
||||||
|
|
||||||
|
return <Base classNames={ getClassNames } { ...rest } />;
|
||||||
|
}
|
36
src/common/Column.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Flex, FlexProps } from './Flex';
|
||||||
|
import { useGridContext } from './GridContext';
|
||||||
|
import { ColumnSizesType } from './types';
|
||||||
|
|
||||||
|
export interface ColumnProps extends FlexProps
|
||||||
|
{
|
||||||
|
size?: ColumnSizesType;
|
||||||
|
column?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Column: FC<ColumnProps> = props =>
|
||||||
|
{
|
||||||
|
const { size = 0, column = true, gap = 2, classNames = [], ...rest } = props;
|
||||||
|
const { isCssGrid = false } = useGridContext();
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [];
|
||||||
|
|
||||||
|
if(size)
|
||||||
|
{
|
||||||
|
let colClassName = `col-${ size }`;
|
||||||
|
|
||||||
|
if(isCssGrid) colClassName = `g-${ colClassName }`;
|
||||||
|
|
||||||
|
newClassNames.push(colClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ size, isCssGrid, classNames ]);
|
||||||
|
|
||||||
|
return <Flex classNames={ getClassNames } column={ column } gap={ gap } { ...rest } />;
|
||||||
|
}
|
54
src/common/Flex.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Base, BaseProps } from './Base';
|
||||||
|
import { AlignItemType, AlignSelfType, JustifyContentType, SpacingType } from './types';
|
||||||
|
|
||||||
|
export interface FlexProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
inline?: boolean;
|
||||||
|
column?: boolean;
|
||||||
|
reverse?: boolean;
|
||||||
|
gap?: SpacingType;
|
||||||
|
center?: boolean;
|
||||||
|
alignSelf?: AlignSelfType;
|
||||||
|
alignItems?: AlignItemType;
|
||||||
|
justifyContent?: JustifyContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Flex: FC<FlexProps> = props =>
|
||||||
|
{
|
||||||
|
const { inline = false, column = undefined, reverse = false, gap = null, center = false, alignSelf = null, alignItems = null, justifyContent = null, classNames = [], ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [];
|
||||||
|
|
||||||
|
if(inline) newClassNames.push('d-inline-flex');
|
||||||
|
else newClassNames.push('d-flex');
|
||||||
|
|
||||||
|
if(column)
|
||||||
|
{
|
||||||
|
if(reverse) newClassNames.push('flex-column-reverse');
|
||||||
|
else newClassNames.push('flex-column');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(reverse) newClassNames.push('flex-row-reverse');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gap) newClassNames.push('gap-' + gap);
|
||||||
|
|
||||||
|
if(alignSelf) newClassNames.push('align-self-' + alignSelf);
|
||||||
|
|
||||||
|
if(alignItems) newClassNames.push('align-items-' + alignItems);
|
||||||
|
|
||||||
|
if(justifyContent) newClassNames.push('justify-content-' + justifyContent);
|
||||||
|
|
||||||
|
if(!alignItems && !justifyContent && center) newClassNames.push('align-items-center', 'justify-content-center');
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ inline, column, reverse, gap, center, alignSelf, alignItems, justifyContent, classNames ]);
|
||||||
|
|
||||||
|
return <Base classNames={ getClassNames } { ...rest } />;
|
||||||
|
}
|
22
src/common/FormGroup.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Flex, FlexProps } from './Flex';
|
||||||
|
|
||||||
|
export interface FormGroupProps extends FlexProps
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FormGroup: FC<FormGroupProps> = props =>
|
||||||
|
{
|
||||||
|
const { classNames = [], ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'form-group' ];
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ classNames ]);
|
||||||
|
|
||||||
|
return <Flex classNames={ getClassNames } { ...rest } />;
|
||||||
|
}
|
64
src/common/Grid.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { CSSProperties } from 'styled-components';
|
||||||
|
import { Base, BaseProps } from './Base';
|
||||||
|
import { GridContextProvider } from './GridContext';
|
||||||
|
import { AlignItemType, AlignSelfType, JustifyContentType, SpacingType } from './types';
|
||||||
|
|
||||||
|
export interface GridProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
inline?: boolean;
|
||||||
|
gap?: SpacingType;
|
||||||
|
maxContent?: boolean;
|
||||||
|
columnCount?: number;
|
||||||
|
center?: boolean;
|
||||||
|
alignSelf?: AlignSelfType;
|
||||||
|
alignItems?: AlignItemType;
|
||||||
|
justifyContent?: JustifyContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Grid: FC<GridProps> = props =>
|
||||||
|
{
|
||||||
|
const { inline = false, gap = 2, maxContent = false, columnCount = 0, center = false, alignSelf = null, alignItems = null, justifyContent = null, fullHeight = true, classNames = [], style = {}, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [];
|
||||||
|
|
||||||
|
if(inline) newClassNames.push('inline-grid');
|
||||||
|
else newClassNames.push('grid');
|
||||||
|
|
||||||
|
if(gap) newClassNames.push('gap-' + gap);
|
||||||
|
else if(gap === 0) newClassNames.push('gap-0');
|
||||||
|
|
||||||
|
if(maxContent) newClassNames.push('flex-basis-max-content');
|
||||||
|
|
||||||
|
if(alignSelf) newClassNames.push('align-self-' + alignSelf);
|
||||||
|
|
||||||
|
if(alignItems) newClassNames.push('align-items-' + alignItems);
|
||||||
|
|
||||||
|
if(justifyContent) newClassNames.push('justify-content-' + justifyContent);
|
||||||
|
|
||||||
|
if(!alignItems && !justifyContent && center) newClassNames.push('align-items-center', 'justify-content-center');
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ inline, gap, maxContent, alignSelf, alignItems, justifyContent, center, classNames ]);
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newStyle: CSSProperties = {};
|
||||||
|
|
||||||
|
if(columnCount) newStyle['--bs-columns'] = columnCount.toString();
|
||||||
|
|
||||||
|
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ columnCount, style ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridContextProvider value={ { isCssGrid: true } }>
|
||||||
|
<Base fullHeight={ fullHeight } classNames={ getClassNames } style={ getStyle } { ...rest } />
|
||||||
|
</GridContextProvider>
|
||||||
|
);
|
||||||
|
}
|
17
src/common/GridContext.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { createContext, FC, ProviderProps, useContext } from 'react';
|
||||||
|
|
||||||
|
export interface IGridContext
|
||||||
|
{
|
||||||
|
isCssGrid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GridContext = createContext<IGridContext>({
|
||||||
|
isCssGrid: false
|
||||||
|
});
|
||||||
|
|
||||||
|
export const GridContextProvider: FC<ProviderProps<IGridContext>> = props =>
|
||||||
|
{
|
||||||
|
return <GridContext.Provider value={ props.value }>{ props.children }</GridContext.Provider>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGridContext = () => useContext(GridContext);
|
63
src/common/Text.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Base, BaseProps } from './Base';
|
||||||
|
import { ColorVariantType, FontSizeType, FontWeightType, TextAlignType } from './types';
|
||||||
|
|
||||||
|
export interface TextProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
variant?: ColorVariantType;
|
||||||
|
fontWeight?: FontWeightType;
|
||||||
|
fontSize?: FontSizeType;
|
||||||
|
align?: TextAlignType;
|
||||||
|
bold?: boolean;
|
||||||
|
underline?: boolean;
|
||||||
|
italics?: boolean;
|
||||||
|
truncate?: boolean;
|
||||||
|
center?: boolean;
|
||||||
|
textEnd?: boolean;
|
||||||
|
small?: boolean;
|
||||||
|
wrap?: boolean;
|
||||||
|
noWrap?: boolean;
|
||||||
|
textBreak?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Text: FC<TextProps> = props =>
|
||||||
|
{
|
||||||
|
const { variant = 'black', fontWeight = null, fontSize = 0, align = null, bold = false, underline = false, italics = false, truncate = false, center = false, textEnd = false, small = false, wrap = false, noWrap = false, textBreak = false, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'd-inline' ];
|
||||||
|
|
||||||
|
if(variant) newClassNames.push('text-' + variant);
|
||||||
|
|
||||||
|
if(bold) newClassNames.push('fw-bold');
|
||||||
|
|
||||||
|
if(fontWeight) newClassNames.push('fw-' + fontWeight);
|
||||||
|
|
||||||
|
if(fontSize) newClassNames.push('fs-' + fontSize);
|
||||||
|
|
||||||
|
if(align) newClassNames.push('text-' + align);
|
||||||
|
|
||||||
|
if(underline) newClassNames.push('text-decoration-underline');
|
||||||
|
|
||||||
|
if(italics) newClassNames.push('fst-italic');
|
||||||
|
|
||||||
|
if(truncate) newClassNames.push('text-truncate');
|
||||||
|
|
||||||
|
if(center) newClassNames.push('text-center');
|
||||||
|
|
||||||
|
if(textEnd) newClassNames.push('text-end');
|
||||||
|
|
||||||
|
if(small) newClassNames.push('small');
|
||||||
|
|
||||||
|
if(wrap) newClassNames.push('text-wrap');
|
||||||
|
|
||||||
|
if(noWrap) newClassNames.push('text-nowrap');
|
||||||
|
|
||||||
|
if(textBreak) newClassNames.push('text-break');
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ variant, fontWeight, fontSize, align, bold, underline, italics, truncate, center, textEnd, small, wrap, noWrap, textBreak ]);
|
||||||
|
|
||||||
|
return <Base classNames={ getClassNames } { ...rest } />;
|
||||||
|
}
|
43
src/common/index.scss
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
.layout-grid-item {
|
||||||
|
height: var(--nitro-grid-column-min-height, 45px);
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: $grid-bg-color;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: $grid-active-border-color !important;
|
||||||
|
background-color: $grid-active-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
|
||||||
|
img {
|
||||||
|
opacity: .5;
|
||||||
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unseen {
|
||||||
|
background-color: rgba($success, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
background-position-y: -35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.has-highlight {
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 50%;
|
||||||
|
background-color: $white;
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
src/common/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export * from './AutoGrid';
|
||||||
|
export * from './Base';
|
||||||
|
export * from './Button';
|
||||||
|
export * from './ButtonGroup';
|
||||||
|
export * from './Column';
|
||||||
|
export * from './Flex';
|
||||||
|
export * from './FormGroup';
|
||||||
|
export * from './Grid';
|
||||||
|
export * from './GridContext';
|
||||||
|
export * from './layout';
|
||||||
|
export * from './Text';
|
||||||
|
export * from './types';
|
||||||
|
export * from './utils';
|
75
src/common/layout/LayoutGridItem.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { ItemCountView } from '../../views/shared/item-count/ItemCountView';
|
||||||
|
import { LimitedEditionStyledNumberView } from '../../views/shared/limited-edition/LimitedEditionStyledNumberView';
|
||||||
|
import { Base } from '../Base';
|
||||||
|
import { Column, ColumnProps } from '../Column';
|
||||||
|
|
||||||
|
export interface LayoutGridItemProps extends ColumnProps
|
||||||
|
{
|
||||||
|
itemImage?: string;
|
||||||
|
itemColor?: string;
|
||||||
|
itemActive?: boolean;
|
||||||
|
itemCount?: number;
|
||||||
|
itemCountMinimum?: number;
|
||||||
|
itemUniqueSoldout?: boolean;
|
||||||
|
itemUniqueNumber?: number;
|
||||||
|
itemUnseen?: boolean;
|
||||||
|
itemHighlight?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LayoutGridItem: FC<LayoutGridItemProps> = props =>
|
||||||
|
{
|
||||||
|
const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemCountMinimum = 1, itemUniqueSoldout = false, itemUniqueNumber = -2, itemUnseen = false, itemHighlight = false, disabled = false, center = true, column = true, style = {}, classNames = [], position = 'relative', overflow = 'hidden', children = null, ...rest } = props;
|
||||||
|
|
||||||
|
const getClassNames = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newClassNames: string[] = [ 'layout-grid-item', 'border', 'border-2', 'border-muted', 'rounded' ];
|
||||||
|
|
||||||
|
if(itemActive) newClassNames.push('active');
|
||||||
|
|
||||||
|
if(itemUniqueSoldout || (itemUniqueNumber > 0)) newClassNames.push('unique-item');
|
||||||
|
|
||||||
|
if(itemUniqueSoldout) newClassNames.push('sold-out');
|
||||||
|
|
||||||
|
if(itemUnseen) newClassNames.push('unseen');
|
||||||
|
|
||||||
|
if(itemHighlight) newClassNames.push('has-highlight');
|
||||||
|
|
||||||
|
if(disabled) newClassNames.push('disabled')
|
||||||
|
|
||||||
|
if(itemImage === null) newClassNames.push('icon', 'loading-icon');
|
||||||
|
|
||||||
|
if(classNames.length) newClassNames.push(...classNames);
|
||||||
|
|
||||||
|
return newClassNames;
|
||||||
|
}, [ itemActive, itemUniqueSoldout, itemUniqueNumber, itemUnseen, itemHighlight, disabled, itemImage, classNames ]);
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
let newStyle = { ...style };
|
||||||
|
|
||||||
|
if(itemImage) newStyle.backgroundImage = `url(${ itemImage })`;
|
||||||
|
|
||||||
|
if(itemColor) newStyle.backgroundColor = itemColor;
|
||||||
|
|
||||||
|
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ style, itemImage, itemColor ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column center={ center } pointer position={ position } overflow={ overflow } column={ column } classNames={ getClassNames } style={ getStyle } { ...rest }>
|
||||||
|
{ (itemCount > itemCountMinimum) &&
|
||||||
|
<ItemCountView count={ itemCount } /> }
|
||||||
|
{ (itemUniqueNumber > 0) &&
|
||||||
|
<>
|
||||||
|
<Base fit className="unique-bg-override" style={ { backgroundImage: `url(${ itemImage })` } } />
|
||||||
|
<div className="position-absolute bottom-0 unique-item-counter">
|
||||||
|
<LimitedEditionStyledNumberView value={ itemUniqueNumber } />
|
||||||
|
</div>
|
||||||
|
</> }
|
||||||
|
{ children }
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
23
src/common/layout/LayoutImage.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { FC, useMemo } from 'react';
|
||||||
|
import { Base, BaseProps } from '../Base';
|
||||||
|
|
||||||
|
export interface LayoutImageProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
imageUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LayoutImage: FC<LayoutImageProps> = props =>
|
||||||
|
{
|
||||||
|
const { imageUrl = null, fit = true, style = null, ...rest } = props;
|
||||||
|
|
||||||
|
const getStyle = useMemo(() =>
|
||||||
|
{
|
||||||
|
const newStyle = { ...style };
|
||||||
|
|
||||||
|
if(imageUrl) newStyle.background = `url(${ imageUrl }) center no-repeat`;
|
||||||
|
|
||||||
|
return newStyle;
|
||||||
|
}, [ style, imageUrl ]);
|
||||||
|
|
||||||
|
return <Base fit={ fit } style={ getStyle } { ...rest } />;
|
||||||
|
}
|
2
src/common/layout/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './LayoutGridItem';
|
||||||
|
export * from './LayoutImage';
|
1
src/common/types/AlignItemType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type AlignItemType = 'start' | 'end' | 'center' | 'baseline' | 'stretch';
|
1
src/common/types/AlignSelfType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type AlignSelfType = 'start' | 'end' | 'center' | 'baseline' | 'stretch';
|
1
src/common/types/ButtonSizeType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type ButtonSizeType = 'lg' | 'sm';
|
1
src/common/types/ColorVariantType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type ColorVariantType = 'primary' | 'success' | 'danger' | 'secondary' | 'link' | 'black' | 'white' | 'dark' | 'warning' | 'muted';
|
1
src/common/types/ColumnSizesType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type ColumnSizesType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
1
src/common/types/FloatType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type FloatType = 'start' | 'end' | 'none';
|
1
src/common/types/FontSizeType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type FontSizeType = 1 | 2 | 3 | 4 | 5 | 6;
|
1
src/common/types/FontWeightType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type FontWeightType = 'bold' | 'bolder' | 'normal' | 'light' | 'lighter';
|
1
src/common/types/JustifyContentType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type JustifyContentType = 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly';
|
1
src/common/types/OverflowType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type OverflowType = 'hidden' | 'auto' | 'unset';
|
1
src/common/types/PositionType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type PositionType = 'static' | 'relative' | 'fixed' | 'absolute' | 'sticky';
|
1
src/common/types/SpacingType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type SpacingType = 0 | 1 | 2 | 3 | 4 | 5;
|
1
src/common/types/TextAlignType.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type TextAlignType = 'start' | 'center' | 'end';
|
13
src/common/types/index.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export * from './AlignItemType';
|
||||||
|
export * from './AlignSelfType';
|
||||||
|
export * from './ButtonSizeType';
|
||||||
|
export * from './ColorVariantType';
|
||||||
|
export * from './ColumnSizesType';
|
||||||
|
export * from './FloatType';
|
||||||
|
export * from './FontSizeType';
|
||||||
|
export * from './FontWeightType';
|
||||||
|
export * from './JustifyContentType';
|
||||||
|
export * from './OverflowType';
|
||||||
|
export * from './PositionType';
|
||||||
|
export * from './SpacingType';
|
||||||
|
export * from './TextAlignType';
|
14
src/common/utils/CreateTransitionToIcon.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { NitroToolbarAnimateIconEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { GetRoomEngine } from '../../api';
|
||||||
|
|
||||||
|
export const CreateTransitionToIcon = (image: HTMLImageElement, fromElement: HTMLElement, icon: string) =>
|
||||||
|
{
|
||||||
|
const bounds = fromElement.getBoundingClientRect();
|
||||||
|
const x = (bounds.x + (bounds.width / 2));
|
||||||
|
const y = (bounds.y + (bounds.height / 2));
|
||||||
|
const event = new NitroToolbarAnimateIconEvent(image, x, y);
|
||||||
|
|
||||||
|
event.iconName = icon;
|
||||||
|
|
||||||
|
GetRoomEngine().events.dispatchEvent(event);
|
||||||
|
}
|
1
src/common/utils/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './CreateTransitionToIcon';
|
@ -3,19 +3,6 @@
|
|||||||
height: $achievement-height;
|
height: $achievement-height;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nitro-achievements-category-grid {
|
|
||||||
--nitro-grid-column-min-width: 80px !important;
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
height: 80px;
|
|
||||||
max-height: 80px;
|
|
||||||
|
|
||||||
.achievement-score {
|
|
||||||
top: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nitro-achievements-back-arrow {
|
.nitro-achievements-back-arrow {
|
||||||
background: url('../../assets/images/achievements/back-arrow.png') no-repeat center;
|
background: url('../../assets/images/achievements/back-arrow.png') no-repeat center;
|
||||||
width: 33px;
|
width: 33px;
|
||||||
@ -23,6 +10,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nitro-achievements-badge-image {
|
.nitro-achievements-badge-image {
|
||||||
width: 80px;
|
width: 80px !important;
|
||||||
height: 80px;
|
height: 80px !important;
|
||||||
}
|
}
|
@ -1,18 +1,21 @@
|
|||||||
import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
|
import { AchievementData, AchievementEvent, AchievementsEvent, AchievementsScoreEvent, RequestAchievementsMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { LocalizeText } from '../../api';
|
import { GetConfiguration, LocalizeText } from '../../api';
|
||||||
|
import { Base } from '../../common/Base';
|
||||||
|
import { Column } from '../../common/Column';
|
||||||
|
import { Flex } from '../../common/Flex';
|
||||||
|
import { Text } from '../../common/Text';
|
||||||
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements';
|
import { AchievementsUIEvent, AchievementsUIUnseenCountEvent } from '../../events/achievements';
|
||||||
import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../hooks';
|
import { BatchUpdates, CreateMessageHook, dispatchUiEvent, SendMessageHook } from '../../hooks';
|
||||||
import { useUiEvent } from '../../hooks/events';
|
import { useUiEvent } from '../../hooks/events';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, NitroLayoutFlexColumn, NitroLayoutGrid, NitroLayoutGridColumn } from '../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView } from '../../layout';
|
||||||
import { NitroLayoutBase } from '../../layout/base';
|
import { NitroLayoutBase } from '../../layout/base';
|
||||||
import { AchievementsViewProps } from './AchievementsView.types';
|
|
||||||
import { AchievementCategory } from './common/AchievementCategory';
|
import { AchievementCategory } from './common/AchievementCategory';
|
||||||
import { AchievementUtilities } from './common/AchievementUtilities';
|
import { AchievementUtilities } from './common/AchievementUtilities';
|
||||||
import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView';
|
import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView';
|
||||||
import { AchievementCategoryView } from './views/category/AchievementCategoryView';
|
import { AchievementCategoryView } from './views/category/AchievementCategoryView';
|
||||||
|
|
||||||
export const AchievementsView: FC<AchievementsViewProps> = props =>
|
export const AchievementsView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ isVisible, setIsVisible ] = useState(false);
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
const [ isInitalized, setIsInitalized ] = useState(false);
|
const [ isInitalized, setIsInitalized ] = useState(false);
|
||||||
@ -170,6 +173,15 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
|
|||||||
return achievementCategories.find(existing => (existing.code === selectedCategoryCode));
|
return achievementCategories.find(existing => (existing.code === selectedCategoryCode));
|
||||||
}, [ achievementCategories, selectedCategoryCode ]);
|
}, [ achievementCategories, selectedCategoryCode ]);
|
||||||
|
|
||||||
|
const getCategoryIcon = useMemo(() =>
|
||||||
|
{
|
||||||
|
if(!getSelectedCategory) return null;
|
||||||
|
|
||||||
|
const imageUrl = GetConfiguration<string>('achievements.images.url');
|
||||||
|
|
||||||
|
return imageUrl.replace('%image%', `achicon_${ getSelectedCategory.code }`);
|
||||||
|
}, [ getSelectedCategory ]);
|
||||||
|
|
||||||
const setAchievementSeen = useCallback((code: string, achievementId: number) =>
|
const setAchievementSeen = useCallback((code: string, achievementId: number) =>
|
||||||
{
|
{
|
||||||
const newCategories = [ ...achievementCategories ];
|
const newCategories = [ ...achievementCategories ];
|
||||||
@ -207,38 +219,27 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
|
|||||||
<NitroCardView uniqueKey="achievements" className="nitro-achievements" simple={ true }>
|
<NitroCardView uniqueKey="achievements" className="nitro-achievements" simple={ true }>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||||
{ getSelectedCategory &&
|
{ getSelectedCategory &&
|
||||||
<NitroCardSubHeaderView className="justify-content-center align-items-center cursor-pointer" gap={ 3 }>
|
<NitroCardSubHeaderView position="relative" className="justify-content-center align-items-center cursor-pointer" gap={ 3 }>
|
||||||
<NitroLayoutBase onClick={ event => setSelectedCategoryCode(null) } className="nitro-achievements-back-arrow" />
|
<NitroLayoutBase onClick={ event => setSelectedCategoryCode(null) } className="nitro-achievements-back-arrow" />
|
||||||
<NitroLayoutFlexColumn className="flex-grow-1">
|
<Column grow gap={ 0 }>
|
||||||
<NitroLayoutBase className="fs-4 text-black fw-bold">
|
<Text fontSize={ 4 } fontWeight="bold" className="text-small">{ LocalizeText(`quests.${ getSelectedCategory.code }.name`) }</Text>
|
||||||
{ LocalizeText(`quests.${ getSelectedCategory.code }.name`) }
|
<Text>{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ getSelectedCategory.getProgress().toString(), getSelectedCategory.getMaxProgress().toString() ]) }</Text>
|
||||||
</NitroLayoutBase>
|
</Column>
|
||||||
<NitroLayoutBase className="text-black">
|
|
||||||
{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ getSelectedCategory.getProgress().toString(), getSelectedCategory.getMaxProgress().toString() ]) }
|
|
||||||
</NitroLayoutBase>
|
|
||||||
</NitroLayoutFlexColumn>
|
|
||||||
</NitroCardSubHeaderView> }
|
</NitroCardSubHeaderView> }
|
||||||
<NitroCardContentView>
|
<NitroCardContentView>
|
||||||
<NitroLayoutGrid>
|
{ !getSelectedCategory &&
|
||||||
<NitroLayoutGridColumn size={ 12 }>
|
<>
|
||||||
{ !getSelectedCategory &&
|
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
|
||||||
<>
|
<Column grow justifyContent="end">
|
||||||
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
|
<Base className="progress" position="relative">
|
||||||
<NitroLayoutFlexColumn className="flex-grow-1 justify-content-end" gap={ 2 }>
|
<Flex fit center position="absolute" className="text-black">{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) }</Flex>
|
||||||
<NitroLayoutBase className="progress">
|
<Base className="progress-bar bg-success" style={ { width: (scaledProgressPercent + '%') }} />
|
||||||
<NitroLayoutBase className="progress-bar" style={ { width: (scaledProgressPercent + '%') }}>
|
</Base>
|
||||||
{ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) }
|
<Text className="bg-muted rounded p-1" center>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
|
||||||
</NitroLayoutBase>
|
</Column>
|
||||||
</NitroLayoutBase>
|
</> }
|
||||||
<NitroLayoutBase className="bg-muted text-black text-center rounded">
|
{ getSelectedCategory &&
|
||||||
{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }
|
<AchievementCategoryView category={ getSelectedCategory } setAchievementSeen={ setAchievementSeen } /> }
|
||||||
</NitroLayoutBase>
|
|
||||||
</NitroLayoutFlexColumn>
|
|
||||||
</> }
|
|
||||||
{ getSelectedCategory &&
|
|
||||||
<AchievementCategoryView category={ getSelectedCategory } setAchievementSeen={ setAchievementSeen } /> }
|
|
||||||
</NitroLayoutGridColumn>
|
|
||||||
</NitroLayoutGrid>
|
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
);
|
);
|
@ -1,7 +1,14 @@
|
|||||||
|
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView';
|
import { BaseProps } from '../../../../common/Base';
|
||||||
|
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
|
||||||
import { AchievementUtilities } from '../../common/AchievementUtilities';
|
import { AchievementUtilities } from '../../common/AchievementUtilities';
|
||||||
import { AchievementBadgeViewProps } from './AchievementBadgeView.types';
|
|
||||||
|
export interface AchievementBadgeViewProps extends BaseProps<HTMLDivElement>
|
||||||
|
{
|
||||||
|
achievement: AchievementData;
|
||||||
|
scale?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export const AchievementBadgeView: FC<AchievementBadgeViewProps> = props =>
|
export const AchievementBadgeView: FC<AchievementBadgeViewProps> = props =>
|
||||||
{
|
{
|
@ -0,0 +1,66 @@
|
|||||||
|
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../../../api';
|
||||||
|
import { Base } from '../../../../common/Base';
|
||||||
|
import { Column } from '../../../../common/Column';
|
||||||
|
import { Flex } from '../../../../common/Flex';
|
||||||
|
import { Text } from '../../../../common/Text';
|
||||||
|
import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
|
||||||
|
import { AchievementUtilities } from '../../common/AchievementUtilities';
|
||||||
|
import { GetAchievementLevel } from '../../common/GetAchievementLevel';
|
||||||
|
import { GetScaledProgressPercent } from '../../common/GetScaledProgressPercent';
|
||||||
|
import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView';
|
||||||
|
|
||||||
|
export interface AchievementDetailsViewProps
|
||||||
|
{
|
||||||
|
achievement: AchievementData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { achievement = null } = props;
|
||||||
|
|
||||||
|
if(!achievement) return null;
|
||||||
|
|
||||||
|
const achievementLevel = GetAchievementLevel(achievement);
|
||||||
|
const scaledProgressPercent = GetScaledProgressPercent(achievement);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex shrink className="bg-muted rounded p-2 text-black" gap={ 2 } overflow="hidden">
|
||||||
|
<Column center>
|
||||||
|
<AchievementBadgeView className="nitro-achievements-badge-image" achievement={ achievement } scale={ 2 } />
|
||||||
|
<Text fontWeight="bold">
|
||||||
|
{ LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ achievementLevel.toString(), achievement.levelCount.toString() ]) }
|
||||||
|
</Text>
|
||||||
|
</Column>
|
||||||
|
<Column fullWidth justifyContent="center" overflow="hidden">
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Text fontWeight="bold" truncate>
|
||||||
|
{ LocalizeBadgeName(AchievementUtilities.getBadgeCode(achievement)) }
|
||||||
|
</Text>
|
||||||
|
<Text truncate>
|
||||||
|
{ LocalizeBadgeDescription(AchievementUtilities.getBadgeCode(achievement)) }
|
||||||
|
</Text>
|
||||||
|
</Column>
|
||||||
|
{ ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
{ (achievement.levelRewardPoints > 0) &&
|
||||||
|
<Flex alignItems="center" gap={ 1 }>
|
||||||
|
<Text truncate className="small">
|
||||||
|
{ LocalizeText('achievements.details.reward') }
|
||||||
|
</Text>
|
||||||
|
<Flex center className="fw-bold small" gap={ 1 }>
|
||||||
|
{ achievement.levelRewardPoints }
|
||||||
|
<CurrencyIcon type={ achievement.levelRewardPointType } />
|
||||||
|
</Flex>
|
||||||
|
</Flex> }
|
||||||
|
{ (achievement.scoreLimit > 0) &&
|
||||||
|
<Base className="progress" position="relative">
|
||||||
|
<Flex fit center position="absolute" className="text-black"> { LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) }</Flex>
|
||||||
|
<Base className="progress-bar" style={ { width: (scaledProgressPercent + '%') }} />
|
||||||
|
</Base> }
|
||||||
|
</Column> }
|
||||||
|
</Column>
|
||||||
|
</Flex>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC } from 'react';
|
||||||
|
import { LayoutGridItem, LayoutGridItemProps } from '../../../../common/layout/LayoutGridItem';
|
||||||
|
import { AchievementBadgeView } from '../achievement-badge/AchievementBadgeView';
|
||||||
|
|
||||||
|
export interface AchievementListItemViewProps extends LayoutGridItemProps
|
||||||
|
{
|
||||||
|
achievement: AchievementData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementListItemView: FC<AchievementListItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { achievement = null, children = null, ...rest } = props;
|
||||||
|
|
||||||
|
if(!achievement) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutGridItem itemCount={ achievement.unseen } itemCountMinimum={ 0 } { ...rest }>
|
||||||
|
<AchievementBadgeView achievement={ achievement } />
|
||||||
|
{ children }
|
||||||
|
</LayoutGridItem>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { AchievementData } from '@nitrots/nitro-renderer';
|
||||||
|
import { Dispatch, FC, SetStateAction } from 'react';
|
||||||
|
import { AutoGrid } from '../../../../common/AutoGrid';
|
||||||
|
import { AchievementListItemView } from './AchievementListItemView';
|
||||||
|
|
||||||
|
export interface AchievementListViewProps
|
||||||
|
{
|
||||||
|
achievements: AchievementData[];
|
||||||
|
selectedAchievementId: number;
|
||||||
|
setSelectedAchievementId: Dispatch<SetStateAction<number>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementListView: FC<AchievementListViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { achievements = null, selectedAchievementId = 0, setSelectedAchievementId = null, children = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AutoGrid columnCount={ 6 } columnMinWidth={ 50 } columnMinHeight={ 50 }>
|
||||||
|
{ achievements && (achievements.length > 0) && achievements.map((achievement, index) =>
|
||||||
|
{
|
||||||
|
return <AchievementListItemView key={ index } achievement={ achievement } itemActive={ (selectedAchievementId === achievement.achievementId) } onClick={ event => setSelectedAchievementId(achievement.achievementId) } />;
|
||||||
|
}) }
|
||||||
|
{ children }
|
||||||
|
</AutoGrid>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import { FC, useCallback, useMemo } from 'react';
|
||||||
|
import { GetConfiguration, LocalizeText } from '../../../../api';
|
||||||
|
import { LayoutGridItem, LayoutGridItemProps } from '../../../../common/layout/LayoutGridItem';
|
||||||
|
import { LayoutImage } from '../../../../common/layout/LayoutImage';
|
||||||
|
import { Text } from '../../../../common/Text';
|
||||||
|
import { AchievementCategory } from '../../common/AchievementCategory';
|
||||||
|
|
||||||
|
export interface AchievementCategoryListItemViewProps extends LayoutGridItemProps
|
||||||
|
{
|
||||||
|
category: AchievementCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementsCategoryListItemView: FC<AchievementCategoryListItemViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { category = null, ...rest } = props;
|
||||||
|
|
||||||
|
const progress = category.getProgress();
|
||||||
|
const maxProgress = category.getMaxProgress();
|
||||||
|
|
||||||
|
const getCategoryImage = useMemo(() =>
|
||||||
|
{
|
||||||
|
const imageUrl = GetConfiguration<string>('achievements.images.url');
|
||||||
|
|
||||||
|
return imageUrl.replace('%image%', `achcategory_${ category.code }_${ ((progress > 0) ? 'active' : 'inactive') }`);
|
||||||
|
}, [ category, progress ]);
|
||||||
|
|
||||||
|
const getTotalUnseen = useCallback(() =>
|
||||||
|
{
|
||||||
|
let unseen = 0;
|
||||||
|
|
||||||
|
for(const achievement of category.achievements) unseen += achievement.unseen;
|
||||||
|
|
||||||
|
return unseen;
|
||||||
|
}, [ category ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LayoutGridItem itemCount={ getTotalUnseen() } itemCountMinimum={ 0 } gap={ 1 } { ...rest }>
|
||||||
|
<Text fullWidth center className="small pt-1">{ LocalizeText(`quests.${ category.code }.name`) }</Text>
|
||||||
|
<LayoutImage position="relative" imageUrl={ getCategoryImage }>
|
||||||
|
<Text fullWidth center position="absolute" variant="white" style={ { fontSize: 12, bottom: 9 } }>{ progress } / { maxProgress }</Text>
|
||||||
|
</LayoutImage>
|
||||||
|
</LayoutGridItem>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { Dispatch, FC, SetStateAction } from 'react';
|
||||||
|
import { AutoGrid } from '../../../../common/AutoGrid';
|
||||||
|
import { AchievementCategory } from '../../common/AchievementCategory';
|
||||||
|
import { AchievementsCategoryListItemView } from './AchievementsCategoryListItemView';
|
||||||
|
|
||||||
|
export interface AchievementsCategoryListViewProps
|
||||||
|
{
|
||||||
|
categories: AchievementCategory[];
|
||||||
|
selectedCategoryCode: string;
|
||||||
|
setSelectedCategoryCode: Dispatch<SetStateAction<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AchievementsCategoryListView: FC<AchievementsCategoryListViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { categories = null, selectedCategoryCode = null, setSelectedCategoryCode = null, children = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AutoGrid columnCount={ 3 } columnMinWidth={ 90 } columnMinHeight={ 100 }>
|
||||||
|
{ categories && (categories.length > 0) && categories.map((category, index) => <AchievementsCategoryListItemView key={ index } category={ category } itemActive={ (selectedCategoryCode === category.code) } onClick={ event => setSelectedCategoryCode(category.code) } /> ) }
|
||||||
|
{ children }
|
||||||
|
</AutoGrid>
|
||||||
|
);
|
||||||
|
};
|
@ -1,8 +1,14 @@
|
|||||||
import { FC, useEffect, useMemo, useState } from 'react';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import { NitroLayoutFlexColumn } from '../../../../layout';
|
import { Column } from '../../../../common/Column';
|
||||||
|
import { AchievementCategory } from '../../common/AchievementCategory';
|
||||||
import { AchievementDetailsView } from '../achievement-details/AchievementDetailsView';
|
import { AchievementDetailsView } from '../achievement-details/AchievementDetailsView';
|
||||||
import { AchievementListView } from '../achievement-list/AchievementListView';
|
import { AchievementListView } from '../achievement-list/AchievementListView';
|
||||||
import { AchievementCategoryViewProps } from './AchievementCategoryView.types';
|
|
||||||
|
export class AchievementCategoryViewProps
|
||||||
|
{
|
||||||
|
category: AchievementCategory;
|
||||||
|
setAchievementSeen: (code: string, achievementId: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export const AchievementCategoryView: FC<AchievementCategoryViewProps> = props =>
|
export const AchievementCategoryView: FC<AchievementCategoryViewProps> = props =>
|
||||||
{
|
{
|
||||||
@ -42,10 +48,10 @@ export const AchievementCategoryView: FC<AchievementCategoryViewProps> = props =
|
|||||||
if(!category) return null;
|
if(!category) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutFlexColumn className="justify-content-between h-100" gap={ 2 }>
|
<Column fullHeight justifyContent="between">
|
||||||
<AchievementListView achievements={ category.achievements } selectedAchievementId={ selectedAchievementId } setSelectedAchievementId={ setSelectedAchievementId } />
|
<AchievementListView achievements={ category.achievements } selectedAchievementId={ selectedAchievementId } setSelectedAchievementId={ setSelectedAchievementId } />
|
||||||
{ getSelectedAchievement &&
|
{ getSelectedAchievement &&
|
||||||
<AchievementDetailsView achievement={ getSelectedAchievement } /> }
|
<AchievementDetailsView achievement={ getSelectedAchievement } /> }
|
||||||
</NitroLayoutFlexColumn>
|
</Column>
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -192,7 +192,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
&.spotlight {
|
&.spotlight-icon {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
height: 305px;
|
height: 305px;
|
||||||
background-position: -5px -5px;
|
background-position: -5px -5px;
|
||||||
@ -212,6 +212,51 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nitro-avatar-editor-wardrobe-figure-preview {
|
||||||
|
background-color: $pale-sky;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
.avatar-image {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -15px;
|
||||||
|
margin: 0 auto;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-shadow {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 25px;
|
||||||
|
width: 40px;
|
||||||
|
height: 20px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.20);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
top: 75%;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: $pale-sky;
|
||||||
|
box-shadow: 0 0 8px 2px rgba($white,.6);
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.nitro-avatar-editor {
|
.nitro-avatar-editor {
|
||||||
width: $avatar-editor-width;
|
width: $avatar-editor-width;
|
||||||
height: $avatar-editor-height;
|
height: $avatar-editor-height;
|
||||||
@ -251,9 +296,11 @@
|
|||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spotlight {
|
.avatar-spotlight {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -10px;
|
top: -10px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -287,71 +334,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.nitro-wardrobe-grid {
|
|
||||||
--nitro-grid-column-min-width: 80px !important;
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
height: 140px;
|
|
||||||
max-height: 140px;
|
|
||||||
background-color: $ghost;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
top: 75%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: $gray-chateau;
|
|
||||||
box-shadow: 0 0 8px 2px rgba($white,.6);
|
|
||||||
transform: scale(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar-image {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
background-position-y: -23px !important;
|
|
||||||
z-index: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.figure-button-container {
|
|
||||||
background-color: $gray-chateau;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-item-container {
|
|
||||||
height: 142px !important;
|
|
||||||
max-height: 142px !important;
|
|
||||||
|
|
||||||
.grid-item {
|
|
||||||
background-color: $ghost;
|
|
||||||
|
|
||||||
.avatar-image {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
background-position-y: -23px !important;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.figure-button-container {
|
|
||||||
background-color: $gray-chateau;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
height: 50%;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background-color: $gray-chateau;
|
|
||||||
box-shadow: 0 0 8px 2px rgba($white,.6);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +1,15 @@
|
|||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer';
|
import { AvatarEditorFigureCategory, FigureSetIdsMessageEvent, GetWardrobeMessageComposer, IAvatarFigureContainer, UserFigureComposer, UserWardrobePageEvent } from '@nitrots/nitro-renderer';
|
||||||
import { FC, useCallback, useEffect, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { GetAvatarRenderManager, GetClubMemberLevel, GetSessionDataManager, LocalizeText } from '../../api';
|
import { GetAvatarRenderManager, GetClubMemberLevel, GetConfiguration, GetSessionDataManager, LocalizeText } from '../../api';
|
||||||
|
import { Button } from '../../common/Button';
|
||||||
|
import { ButtonGroup } from '../../common/ButtonGroup';
|
||||||
|
import { Column } from '../../common/Column';
|
||||||
|
import { Grid } from '../../common/Grid';
|
||||||
import { AvatarEditorEvent } from '../../events/avatar-editor';
|
import { AvatarEditorEvent } from '../../events/avatar-editor';
|
||||||
import { CreateMessageHook, SendMessageHook } from '../../hooks';
|
import { CreateMessageHook, SendMessageHook } from '../../hooks';
|
||||||
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
import { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, NitroLayoutGrid, NitroLayoutGridColumn } from '../../layout';
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../layout';
|
||||||
import { AvatarEditorViewProps } from './AvatarEditorView.types';
|
|
||||||
import { AvatarEditorAction } from './common/AvatarEditorAction';
|
import { AvatarEditorAction } from './common/AvatarEditorAction';
|
||||||
import { AvatarEditorUtilities } from './common/AvatarEditorUtilities';
|
import { AvatarEditorUtilities } from './common/AvatarEditorUtilities';
|
||||||
import { BodyModel } from './common/BodyModel';
|
import { BodyModel } from './common/BodyModel';
|
||||||
@ -15,20 +19,14 @@ import { HeadModel } from './common/HeadModel';
|
|||||||
import { IAvatarEditorCategoryModel } from './common/IAvatarEditorCategoryModel';
|
import { IAvatarEditorCategoryModel } from './common/IAvatarEditorCategoryModel';
|
||||||
import { LegModel } from './common/LegModel';
|
import { LegModel } from './common/LegModel';
|
||||||
import { TorsoModel } from './common/TorsoModel';
|
import { TorsoModel } from './common/TorsoModel';
|
||||||
import { AvatarEditorFigureActionsView } from './views/figure-actions/AvatarEditorFigureActionsView';
|
|
||||||
import { AvatarEditorFigurePreviewView } from './views/figure-preview/AvatarEditorFigurePreviewView';
|
import { AvatarEditorFigurePreviewView } from './views/figure-preview/AvatarEditorFigurePreviewView';
|
||||||
import { AvatarEditorModelView } from './views/model/AvatarEditorModelView';
|
import { AvatarEditorModelView } from './views/model/AvatarEditorModelView';
|
||||||
import { AvatarEditorWardrobeView } from './views/wardrobe/AvatarEditorWardrobeView';
|
import { AvatarEditorWardrobeView } from './views/wardrobe/AvatarEditorWardrobeView';
|
||||||
|
|
||||||
const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007';
|
const DEFAULT_MALE_FIGURE: string = 'hr-100.hd-180-7.ch-215-66.lg-270-79.sh-305-62.ha-1002-70.wa-2007';
|
||||||
const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68';
|
const DEFAULT_FEMALE_FIGURE: string = 'hr-515-33.hd-600-1.ch-635-70.lg-716-66-62.sh-735-68';
|
||||||
const MAX_SAVED_FIGURES: number = 10;
|
|
||||||
const ACTION_CLEAR = 'action_clear';
|
|
||||||
const ACTION_RESET = 'action_reset';
|
|
||||||
const ACTION_RANDOMIZE = 'action_randomize';
|
|
||||||
const ACTION_SAVE = 'action_save';
|
|
||||||
|
|
||||||
export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
export const AvatarEditorView: FC<{}> = props =>
|
||||||
{
|
{
|
||||||
const [ isVisible, setIsVisible ] = useState(false);
|
const [ isVisible, setIsVisible ] = useState(false);
|
||||||
const [ figures, setFigures ] = useState<Map<string, FigureData>>(null);
|
const [ figures, setFigures ] = useState<Map<string, FigureData>>(null);
|
||||||
@ -37,13 +35,15 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
const [ activeCategory, setActiveCategory ] = useState<IAvatarEditorCategoryModel>(null);
|
const [ activeCategory, setActiveCategory ] = useState<IAvatarEditorCategoryModel>(null);
|
||||||
const [ figureSetIds, setFigureSetIds ] = useState<number[]>([]);
|
const [ figureSetIds, setFigureSetIds ] = useState<number[]>([]);
|
||||||
const [ boundFurnitureNames, setBoundFurnitureNames ] = useState<string[]>([]);
|
const [ boundFurnitureNames, setBoundFurnitureNames ] = useState<string[]>([]);
|
||||||
const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>(new Array(MAX_SAVED_FIGURES));
|
const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>([]);
|
||||||
const [ isWardrobeVisible, setIsWardrobeVisible ] = useState(false);
|
const [ isWardrobeVisible, setIsWardrobeVisible ] = useState(false);
|
||||||
const [ lastFigure, setLastFigure ] = useState<string>(null);
|
const [ lastFigure, setLastFigure ] = useState<string>(null);
|
||||||
const [ lastGender, setLastGender ] = useState<string>(null);
|
const [ lastGender, setLastGender ] = useState<string>(null);
|
||||||
const [ needsReset, setNeedsReset ] = useState(false);
|
const [ needsReset, setNeedsReset ] = useState(false);
|
||||||
const [ isInitalized, setIsInitalized ] = useState(false);
|
const [ isInitalized, setIsInitalized ] = useState(false);
|
||||||
|
|
||||||
|
const maxWardrobeSlots = useMemo(() => GetConfiguration<number>('avatar.wardrobe.max.slots', 10), []);
|
||||||
|
|
||||||
const onAvatarEditorEvent = useCallback((event: AvatarEditorEvent) =>
|
const onAvatarEditorEvent = useCallback((event: AvatarEditorEvent) =>
|
||||||
{
|
{
|
||||||
switch(event.type)
|
switch(event.type)
|
||||||
@ -89,7 +89,7 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
while(i < MAX_SAVED_FIGURES)
|
while(i < maxWardrobeSlots)
|
||||||
{
|
{
|
||||||
savedFigures.push([ null, null ]);
|
savedFigures.push([ null, null ]);
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSavedFigures(savedFigures)
|
setSavedFigures(savedFigures)
|
||||||
}, []);
|
}, [ maxWardrobeSlots ]);
|
||||||
|
|
||||||
CreateMessageHook(UserWardrobePageEvent, onUserWardrobePageEvent);
|
CreateMessageHook(UserWardrobePageEvent, onUserWardrobePageEvent);
|
||||||
|
|
||||||
@ -195,6 +195,11 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
setFigureData(figures.get(gender));
|
setFigureData(figures.get(gender));
|
||||||
}, [ figures ]);
|
}, [ figures ]);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setSavedFigures(new Array(maxWardrobeSlots));
|
||||||
|
}, [ maxWardrobeSlots ]);
|
||||||
|
|
||||||
useEffect(() =>
|
useEffect(() =>
|
||||||
{
|
{
|
||||||
if(!isWardrobeVisible) return;
|
if(!isWardrobeVisible) return;
|
||||||
@ -285,18 +290,33 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
|||||||
</NitroCardTabsItemView>
|
</NitroCardTabsItemView>
|
||||||
</NitroCardTabsView>
|
</NitroCardTabsView>
|
||||||
<NitroCardContentView>
|
<NitroCardContentView>
|
||||||
<NitroLayoutGrid>
|
<Grid>
|
||||||
<NitroLayoutGridColumn size={ 9 }>
|
<Column size={ 9 } overflow="hidden">
|
||||||
{ (activeCategory && !isWardrobeVisible) &&
|
{ (activeCategory && !isWardrobeVisible) &&
|
||||||
<AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } setGender={ setGender } /> }
|
<AvatarEditorModelView model={ activeCategory } gender={ figureData.gender } setGender={ setGender } /> }
|
||||||
{ isWardrobeVisible &&
|
{ isWardrobeVisible &&
|
||||||
<AvatarEditorWardrobeView figureData={ figureData } savedFigures={ savedFigures } setSavedFigures={ setSavedFigures } loadAvatarInEditor={ loadAvatarInEditor } /> }
|
<AvatarEditorWardrobeView figureData={ figureData } savedFigures={ savedFigures } setSavedFigures={ setSavedFigures } loadAvatarInEditor={ loadAvatarInEditor } /> }
|
||||||
</NitroLayoutGridColumn>
|
</Column>
|
||||||
<NitroLayoutGridColumn overflow="hidden" size={ 3 }>
|
<Column size={ 3 } overflow="hidden">
|
||||||
<AvatarEditorFigurePreviewView figureData={ figureData } />
|
<AvatarEditorFigurePreviewView figureData={ figureData } />
|
||||||
<AvatarEditorFigureActionsView processAction={ processAction } />
|
<Column grow gap={ 1 }>
|
||||||
</NitroLayoutGridColumn>
|
<ButtonGroup>
|
||||||
</NitroLayoutGrid>
|
<Button variant="secondary" size="sm" onClick={ event => processAction(AvatarEditorAction.ACTION_RESET) }>
|
||||||
|
<FontAwesomeIcon icon="undo" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="sm" onClick={ event => processAction(AvatarEditorAction.ACTION_CLEAR) }>
|
||||||
|
<FontAwesomeIcon icon="trash" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="sm" onClick={ event => processAction(AvatarEditorAction.ACTION_RANDOMIZE) }>
|
||||||
|
<FontAwesomeIcon icon="dice" />
|
||||||
|
</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
<Button className="w-100" variant="success" size="sm" onClick={ event => processAction(AvatarEditorAction.ACTION_SAVE) }>
|
||||||
|
{ LocalizeText('avatareditor.save') }
|
||||||
|
</Button>
|
||||||
|
</Column>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView>
|
</NitroCardView>
|
||||||
);
|
);
|