mirror of
https://github.com/LasCC/HackTools.git
synced 2025-09-06 23:41:03 +00:00
NEW ✅ - Dynamic theme selection
This commit is contained in:
69
package.json
69
package.json
@@ -13,60 +13,61 @@
|
||||
"author": "Ludovic COULON & Riadh BOUCHAHOUA",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.14.8",
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/cli": "^7.16.0",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/helper-call-delegate": "^7.12.13",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@babel/preset-react": "^7.14.5",
|
||||
"@babel/preset-typescript": "^7.9.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.0",
|
||||
"@babel/preset-env": "^7.16.4",
|
||||
"@babel/preset-react": "^7.16.0",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.2",
|
||||
"@types/antd": "^1.0.0",
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
"@types/pretty": "^2.0.0",
|
||||
"@types/react-query": "^1.2.9",
|
||||
"@types/react-syntax-highlighter": "^13.5.2",
|
||||
"@types/use-persisted-state": "^0.3.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"clean-webpack-plugin": "^4.0.0-alpha.0",
|
||||
"copy-webpack-plugin": "^9.0.1",
|
||||
"css-loader": "^6.2.0",
|
||||
"babel-loader": "^8.2.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^10.0.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"style-loader": "^3.2.1",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^5.50.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.0.0"
|
||||
"react-refresh": "^0.11.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^5.64.1",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.5.0"
|
||||
},
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"browserslist": [ ">0.2%", "not dead", "not op_mini all" ],
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "4.6.2",
|
||||
"@ant-design/icons": "4.7.0",
|
||||
"@hot-loader/react-dom": "^17.0.1",
|
||||
"@types/chrome": "^0.0.154",
|
||||
"@types/jest": "^27.0.1",
|
||||
"@types/node": "^16.6.1",
|
||||
"@types/react": "^17.0.18",
|
||||
"@types/chrome": "^0.0.164",
|
||||
"@types/jest": "^27.0.3",
|
||||
"@types/node": "^16.11.9",
|
||||
"@types/react": "^17.0.27",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"antd": "4.16.12",
|
||||
"antd": "4.16.13",
|
||||
"antd-mask-input": "0.1.15",
|
||||
"axios": "^0.21.1",
|
||||
"axios": "^0.24.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"escape-quotes": "^1.0.2",
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"pretty": "^2.0.0",
|
||||
"rc-queue-anim": "^2.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-chrome-extension-router": "^1.2.0",
|
||||
"react": "latest",
|
||||
"react-chrome-extension-router": "^1.3.1",
|
||||
"react-clipboard.js": "2.0.16",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-query": "^3.19.6",
|
||||
"react-syntax-highlighter": "^15.4.3",
|
||||
"react-dom": "latest",
|
||||
"react-query": "^3.33.1",
|
||||
"react-refresh-typescript": "^2.0.2",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"sm3": "^1.0.3",
|
||||
"ts-loader": "^9.2.5",
|
||||
"ts-loader": "^9.2.6",
|
||||
"use-persisted-state": "^0.3.3"
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,3 @@
|
||||
@import '~antd/dist/antd.dark.min.css';
|
||||
|
||||
.logo {
|
||||
height: 32px;
|
||||
margin: 16px;
|
||||
@@ -26,8 +24,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ant-badge {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
.site-layout .site-layout-background {
|
||||
background: #0f0f0f !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.ant-menu-inline .ant-menu-item:not(:last-child),
|
||||
@@ -206,5 +207,4 @@ h5.ant-typography,
|
||||
body {
|
||||
min-width: 750px;
|
||||
min-height: auto;
|
||||
background-color: black;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Layout, Menu, Typography, Button, Badge } from 'antd';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Layout, Menu, Typography, Button, Badge, Select, } from 'antd';
|
||||
import { CopyrightCircleOutlined, FullscreenOutlined, ArrowsAltOutlined } from '@ant-design/icons';
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
import { goTo } from 'react-chrome-extension-router';
|
||||
@@ -7,7 +7,6 @@ import ReverseShell from './linux/ReverseShell';
|
||||
import PhpReverseShell from './web/PhpReverseShell';
|
||||
import TtySpawnShell from './linux/TtySpawnShell';
|
||||
import Base64Encode from './encoding/Base64Encode';
|
||||
import HexEncode from './encoding/HexEncode';
|
||||
import Hashing from './encoding/Hashing';
|
||||
import LinuxCommands from './linux/LinuxCommands';
|
||||
import PowershellCommands from './linux/PowershellCommands';
|
||||
@@ -20,6 +19,8 @@ import FileTransfer from './file_transfer/File_transfer';
|
||||
import PersistedState from 'use-persisted-state';
|
||||
import MSFBuilder from './linux/MSFBuilder';
|
||||
import HTTPUtils from './http_utils/HTTP-Utils';
|
||||
import DynamicTheme from '../theming';
|
||||
import { themes } from '../themes';
|
||||
|
||||
const { Paragraph } = Typography;
|
||||
const { Sider, Content, Footer } = Layout;
|
||||
@@ -27,9 +28,23 @@ const IconFont = createFromIconfontCN({
|
||||
scriptUrl: [ './iconfont.js' ]
|
||||
} );
|
||||
|
||||
const defaultTheme = themes[ 0 ];
|
||||
const options = themes.map( ( theme ) => ( {
|
||||
label: theme.displayName,
|
||||
value: theme.id
|
||||
} ) );
|
||||
|
||||
export default function LayoutApp ( props: {
|
||||
children: boolean | React.ReactChild | React.ReactFragment | React.ReactPortal | null | undefined;
|
||||
} ) {
|
||||
|
||||
const useDefaultTheme = PersistedState( 'default_colored_theme' );
|
||||
const [ themeId, setThemeId ] = useDefaultTheme( defaultTheme.id );
|
||||
const selectThemeId = useCallback(
|
||||
( option ) => setThemeId( option as string ),
|
||||
[]
|
||||
);
|
||||
|
||||
interface IRouterComponent {
|
||||
key: string;
|
||||
icon: JSX.Element;
|
||||
@@ -64,7 +79,11 @@ export default function LayoutApp(props: {
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
icon: <IconFont type='icon-powershell' style={{ fontSize: '1.5em', marginTop: 3 }} />,
|
||||
icon: (
|
||||
<Badge dot size='default' style={{ transform: `translate(3px, 5px)` }}>
|
||||
<IconFont type='icon-powershell' style={{ fontSize: '1.5em', marginTop: 3 }} />
|
||||
</Badge>
|
||||
),
|
||||
name: 'PowerShell Commands',
|
||||
componentRoute: PowershellCommands
|
||||
},
|
||||
@@ -110,7 +129,7 @@ export default function LayoutApp(props: {
|
||||
name: 'Feed RSS',
|
||||
componentRoute: FeedRSS
|
||||
},
|
||||
{
|
||||
/* {
|
||||
key: '13',
|
||||
icon: (
|
||||
<Badge dot size='default' style={{ transform: `translate(3px, 5px)` }}>
|
||||
@@ -119,9 +138,9 @@ export default function LayoutApp(props: {
|
||||
),
|
||||
name: 'HTTP Repeater',
|
||||
componentRoute: HTTPUtils
|
||||
},
|
||||
}, */
|
||||
{
|
||||
key: '14',
|
||||
key: '13',
|
||||
icon: (
|
||||
<Badge dot size='default' style={{ transform: `translate(3px, 5px)` }}>
|
||||
<IconFont type='icon-shield' style={{ fontSize: '1.5em', marginTop: 3 }} />
|
||||
@@ -131,7 +150,7 @@ export default function LayoutApp(props: {
|
||||
componentRoute: MSFBuilder
|
||||
},
|
||||
{
|
||||
key: '15',
|
||||
key: '14',
|
||||
icon: <IconFont type='icon-about' style={{ fontSize: '1.5em', marginTop: 3 }} />,
|
||||
name: 'About us',
|
||||
componentRoute: AboutUs
|
||||
@@ -215,6 +234,13 @@ export default function LayoutApp(props: {
|
||||
Fullscreen mode
|
||||
</a>
|
||||
</Button>
|
||||
<DynamicTheme themes={themes} value={themeId} />
|
||||
<Select
|
||||
value={themeId}
|
||||
style={{ minWidth: 200 }}
|
||||
options={options}
|
||||
onSelect={selectThemeId}
|
||||
/>
|
||||
<Button icon={<ArrowsAltOutlined style={{ margin: 5 }} />} onClick={() => windowMode()} type='link'>
|
||||
Pop-up mode
|
||||
</Button>
|
||||
|
@@ -56,13 +56,17 @@ export default function PowershellCommands() {
|
||||
const ACL_gpoedit_rights = `Get-NetGPO | %{Get-ObjectAcl -ResolveGUIDs -Name $_.Name}`;
|
||||
const ACL_passwd_edit_rights = `Get-ObjectAcl -SamAccountName labuser -ResolveGUIDs -RightsFilter "ResetPassword"`;
|
||||
|
||||
// dump user accounts
|
||||
const local_recon_ldifde = `ldifde -d "OU=THING,DC=CHANGE,DC=ME" -p subtree -f dump.ldf`
|
||||
const local_recon_csvde = `csvde -d "OU=THING,DC=CHANGE,DC=ME" -p subtree -f dump.csv`
|
||||
|
||||
return (
|
||||
<QueueAnim delay={300} duration={1500}>
|
||||
<Title level={2} style={{ fontWeight: 'bold', margin: 15 }}>
|
||||
Powershell handy commands
|
||||
</Title>
|
||||
<Paragraph style={{ margin: 15 }}>List of useful Powershell commands</Paragraph>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'></Divider>
|
||||
<div
|
||||
key='a'
|
||||
style={{
|
||||
@@ -92,8 +96,7 @@ export default function PowershellCommands() {
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{envVar_cmd}
|
||||
</Paragraph>
|
||||
<Divider dashed />
|
||||
<Title level={4}>HTTP download (wget like)</Title>
|
||||
<Divider orientation='center'>HTTP download (wget like)</Divider>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{powershell_http_dl}
|
||||
</Paragraph>
|
||||
@@ -101,8 +104,7 @@ export default function PowershellCommands() {
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{cmd_cert_http_dl}
|
||||
</Paragraph>
|
||||
<Divider dashed />
|
||||
<Title level={4}>WLAN enumeration</Title>
|
||||
<Divider orientation='center'>WLAN enumeration</Divider>
|
||||
{wlan_creddump.map( ( k, i ) => {
|
||||
return (
|
||||
<Paragraph key={i} copyable code editable ellipsis={true}>
|
||||
@@ -112,20 +114,18 @@ export default function PowershellCommands() {
|
||||
} )}
|
||||
</div>
|
||||
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Active Directory enumeration</Divider>
|
||||
<div
|
||||
key='b'
|
||||
style={{
|
||||
padding: 15
|
||||
}}
|
||||
>
|
||||
<Title level={2}>Active Directory enumeration</Title>
|
||||
<Paragraph>Require Powerview.ps1</Paragraph>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{power_view_repo}
|
||||
</Paragraph>
|
||||
<Title level={4}>Domain enumeration</Title>
|
||||
|
||||
<Text strong># Domain enumeration</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{domain_name}
|
||||
</Paragraph>
|
||||
@@ -155,18 +155,14 @@ export default function PowershellCommands() {
|
||||
{domain_trust}
|
||||
</Paragraph>
|
||||
|
||||
<Divider dashed />
|
||||
|
||||
<Title level={4}> GPO enumeration</Title>
|
||||
<Divider orientation='center'>GPO enumeration</Divider>
|
||||
|
||||
<Text strong># GPO applied to the machine</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{gpo_enum}
|
||||
</Paragraph>
|
||||
|
||||
<Divider dashed />
|
||||
|
||||
<Title level={4}> Password enumeration</Title>
|
||||
<Divider orientation='center'>Password enumeration</Divider>
|
||||
|
||||
<Text strong># Last Password Set date</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
@@ -176,9 +172,7 @@ export default function PowershellCommands() {
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{user_desc_harvest}
|
||||
</Paragraph>
|
||||
<Divider dashed />
|
||||
|
||||
<Title level={4}> Computer enumeration</Title>
|
||||
<Divider orientation='center'>Computer enumeration</Divider>
|
||||
|
||||
<Text strong># List Computers of the Domain</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
@@ -193,9 +187,7 @@ export default function PowershellCommands() {
|
||||
{domain_win7U_computers}
|
||||
</Paragraph>
|
||||
|
||||
<Divider dashed />
|
||||
|
||||
<Title level={4}> Admin groups and account enumeration</Title>
|
||||
<Divider orientation='center'>Admin groups and account enumeration</Divider>
|
||||
|
||||
<Text strong># List Domain Admin members</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
@@ -215,9 +207,7 @@ export default function PowershellCommands() {
|
||||
{user_group_membership}
|
||||
</Paragraph>
|
||||
|
||||
<Divider dashed />
|
||||
|
||||
<Title level={4}> ACL enumeration</Title>
|
||||
<Divider orientation='center'>ACL enumeration</Divider>
|
||||
|
||||
<Text strong># User ACL </Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
@@ -233,6 +223,16 @@ export default function PowershellCommands() {
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{ACL_passwd_edit_rights}
|
||||
</Paragraph>
|
||||
|
||||
<Divider orientation='center'>Local reconnaissance</Divider>
|
||||
<Text strong># Export user accounts with ldifde</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{local_recon_ldifde}
|
||||
</Paragraph>
|
||||
<Text strong># Export user accounts with csvde</Text>
|
||||
<Paragraph copyable code editable ellipsis={true}>
|
||||
{local_recon_csvde}
|
||||
</Paragraph>
|
||||
</div>
|
||||
</QueueAnim>
|
||||
);
|
||||
|
@@ -68,11 +68,8 @@ export default function ReverseShell() {
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Bash</Divider>
|
||||
<div style={{ padding: 10, marginTop: 15 }} key='a'>
|
||||
<Title level={3}>
|
||||
Bash <IconFont type='icon-gnubash' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{bash_rshell}
|
||||
</Paragraph>
|
||||
@@ -95,11 +92,8 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Zsh</Divider>
|
||||
<div style={{ padding: 10, marginTop: 15 }} key='a'>
|
||||
<Title level={3}>
|
||||
Zsh <IconFont type='icon-command-line' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{zsh_rshell}
|
||||
</Paragraph>
|
||||
@@ -122,11 +116,8 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Netcat</Divider>
|
||||
<div style={{ padding: 10, marginTop: 15 }} key='b'>
|
||||
<Title level={3}>
|
||||
Netcat <IconFont type='icon-command-line' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{netcat_rshell}
|
||||
</Paragraph>
|
||||
@@ -149,7 +140,7 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>PHP</Divider>
|
||||
<div
|
||||
key='c'
|
||||
style={{
|
||||
@@ -157,9 +148,6 @@ export default function ReverseShell() {
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<Title level={3}>
|
||||
PHP <IconFont type='icon-php' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{php_rshell}
|
||||
</Paragraph>
|
||||
@@ -184,11 +172,8 @@ export default function ReverseShell() {
|
||||
</Clipboard>
|
||||
</div>
|
||||
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>PowerShell</Divider>
|
||||
<div style={{ padding: 10, marginTop: 15 }} key='a'>
|
||||
<Title level={3}>
|
||||
PowerShell <IconFont type='icon-powershell' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{PS_rshell}
|
||||
</Paragraph>
|
||||
@@ -218,10 +203,7 @@ export default function ReverseShell() {
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<Divider dashed />
|
||||
<Title level={3}>
|
||||
Perl <IconFont type='icon-perl' />
|
||||
</Title>
|
||||
<Divider orientation='center'>Perl</Divider>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{perl_rshell}
|
||||
</Paragraph>
|
||||
@@ -245,7 +227,7 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Python</Divider>
|
||||
<div
|
||||
key='e'
|
||||
style={{
|
||||
@@ -253,9 +235,6 @@ export default function ReverseShell() {
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<Title level={3}>
|
||||
Python <IconFont type='icon-python' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{python_rshell}
|
||||
</Paragraph>
|
||||
@@ -279,7 +258,7 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Ruby</Divider>
|
||||
<div
|
||||
key='f'
|
||||
style={{
|
||||
@@ -287,9 +266,6 @@ export default function ReverseShell() {
|
||||
marginTop: 15
|
||||
}}
|
||||
>
|
||||
<Title level={3}>
|
||||
Ruby <IconFont type='icon-ruby' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{ruby_rshell}
|
||||
</Paragraph>
|
||||
@@ -313,11 +289,8 @@ export default function ReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider dashed />
|
||||
<Divider orientation='center'>Telnet</Divider>
|
||||
<div style={{ padding: 15, marginTop: 15 }} key='g'>
|
||||
<Title level={3}>
|
||||
Telnet <IconFont type='icon-lvzhou_yuanchengTelnet' />
|
||||
</Title>
|
||||
<Paragraph code ellipsis={true}>
|
||||
{telnet_rshell}
|
||||
</Paragraph>
|
||||
|
@@ -275,7 +275,7 @@ export default function PhpReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider orientation="left">Basic RCE</Divider>
|
||||
<Divider orientation="center">Basic RCE</Divider>
|
||||
<div
|
||||
key='b'
|
||||
style={{
|
||||
@@ -316,7 +316,7 @@ export default function PhpReverseShell() {
|
||||
</Button>
|
||||
</Clipboard>
|
||||
</div>
|
||||
<Divider orientation="left">Web Shell</Divider>
|
||||
<Divider orientation="center">Web Shell</Divider>
|
||||
<div
|
||||
key='c'
|
||||
style={{
|
||||
@@ -352,7 +352,7 @@ export default function PhpReverseShell() {
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
<Divider orientation="left">Obfuscated PHP Web Shell</Divider>
|
||||
<Divider orientation="center">Obfuscated PHP Web Shell</Divider>
|
||||
<div
|
||||
key='d'
|
||||
style={{
|
||||
|
7
src/themes/DarkCompactTheme.tsx
Normal file
7
src/themes/DarkCompactTheme.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import styles from './dark-compact.theme.less';
|
||||
import { useApplyStyles } from '../theming';
|
||||
|
||||
export default function DarkTheme () {
|
||||
useApplyStyles( styles );
|
||||
return null;
|
||||
}
|
8
src/themes/DarkTheme.tsx
Normal file
8
src/themes/DarkTheme.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import styles from './dark-compact.theme.less';
|
||||
import { useApplyStyles } from '../theming';
|
||||
|
||||
export default function DarkTheme () {
|
||||
useApplyStyles( styles );
|
||||
return null;
|
||||
}
|
8
src/themes/LightCompactTheme.tsx
Normal file
8
src/themes/LightCompactTheme.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import styles from './light-compact.theme.less';
|
||||
import { useApplyStyles } from '../theming';
|
||||
|
||||
export default function LightCompactTheme () {
|
||||
useApplyStyles( styles );
|
||||
return null;
|
||||
}
|
8
src/themes/LightTheme.tsx
Normal file
8
src/themes/LightTheme.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import styles from './light.theme.less';
|
||||
import { useApplyStyles } from '../theming';
|
||||
|
||||
export default function DarkTheme () {
|
||||
useApplyStyles( styles );
|
||||
return null;
|
||||
}
|
6
src/themes/dark-compact.theme.less
Normal file
6
src/themes/dark-compact.theme.less
Normal file
@@ -0,0 +1,6 @@
|
||||
@import '../../node_modules/antd/dist/antd.dark.less';
|
||||
@import '../../node_modules/antd/dist/antd.compact.less';
|
||||
.site-layout .site-layout-background {
|
||||
background: #0f0f0f;
|
||||
border-radius: 10px;
|
||||
}
|
5
src/themes/dark.theme.less
Normal file
5
src/themes/dark.theme.less
Normal file
@@ -0,0 +1,5 @@
|
||||
@import '../../node_modules/antd/dist/antd.dark.less';
|
||||
.site-layout .site-layout-background {
|
||||
background: #0f0f0f;
|
||||
border-radius: 10px;
|
||||
}
|
29
src/themes/index.ts
Normal file
29
src/themes/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { lazy } from 'react';
|
||||
import { Theme } from '../theming/types';
|
||||
|
||||
export const themes: Theme[] = [
|
||||
{
|
||||
id: 'light', // used as value in the select
|
||||
displayName: 'Light', // used as label in the select
|
||||
filename: 'light.theme.less',
|
||||
component: lazy(() => import('../themes/LightTheme'))
|
||||
},
|
||||
{
|
||||
id: 'lightCompact',
|
||||
displayName: 'Light Compact',
|
||||
filename: 'light.theme.less',
|
||||
component: lazy(() => import('../themes/LightCompactTheme'))
|
||||
},
|
||||
{
|
||||
id: 'dark',
|
||||
displayName: 'Dark',
|
||||
filename: 'light.theme.less',
|
||||
component: lazy(() => import('../themes/DarkTheme'))
|
||||
},
|
||||
{
|
||||
id: 'darkCompact',
|
||||
displayName: 'Dark Compact',
|
||||
filename: 'light.theme.less',
|
||||
component: lazy(() => import('../themes/DarkCompactTheme'))
|
||||
}
|
||||
];
|
9
src/themes/light-compact.theme.less
Normal file
9
src/themes/light-compact.theme.less
Normal file
@@ -0,0 +1,9 @@
|
||||
@import '../../node_modules/antd/dist/antd.compact.less';
|
||||
.site-layout .site-layout-background {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.ant-divider-dashed {
|
||||
border-color: rgba(0, 0, 0, 0.20);
|
||||
}
|
9
src/themes/light.theme.less
Normal file
9
src/themes/light.theme.less
Normal file
@@ -0,0 +1,9 @@
|
||||
@import '../../node_modules/antd/dist/antd.less';
|
||||
.site-layout .site-layout-background {
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.ant-divider-dashed {
|
||||
border-color: rgba(0, 0, 0, 0.20);
|
||||
}
|
18
src/theming/LoadingIndicator.tsx
Normal file
18
src/theming/LoadingIndicator.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import * as React from 'react';
|
||||
import { Spin, SpinProps } from 'antd';
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
export default function LoadingIndicator ( props: SpinProps ) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: '100%',
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -520%)'
|
||||
}}>
|
||||
<Spin indicator={<LoadingOutlined />} {...props} />
|
||||
</div>
|
||||
);
|
||||
}
|
26
src/theming/index.tsx
Normal file
26
src/theming/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import * as React from 'react';
|
||||
import { Suspense, useEffect } from 'react';
|
||||
import { LazyStyle, Theme } from './types';
|
||||
import LoadingIndicator from './LoadingIndicator';
|
||||
|
||||
export function useApplyStyles ( styles: LazyStyle ): void {
|
||||
useEffect( () => {
|
||||
styles.use();
|
||||
return () => styles.unuse();
|
||||
} );
|
||||
}
|
||||
|
||||
interface DynamicThemeProps {
|
||||
themes: Theme[],
|
||||
value: string,
|
||||
}
|
||||
|
||||
export default function DynamicTheme ( { themes, value }: DynamicThemeProps ) {
|
||||
const Component = themes.find( theme => theme.id === value )?.component;
|
||||
|
||||
return (
|
||||
<Suspense fallback={<LoadingIndicator tip="loading" />}>
|
||||
<Component />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
13
src/theming/types.ts
Normal file
13
src/theming/types.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export interface LazyStyle {
|
||||
use: () => void;
|
||||
unuse: () => void;
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
id: string;
|
||||
displayName: string;
|
||||
filename: string;
|
||||
component: ReactNode;
|
||||
}
|
@@ -2,13 +2,27 @@
|
||||
"compilerOptions": {
|
||||
"jsx": "react",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noImplicitAny": false,
|
||||
"outDir": "./dist/",
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"esModuleInterop": true,
|
||||
"baseUrl": "src",
|
||||
"sourceMap": true,
|
||||
"target": "es5",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"paths": {
|
||||
"compforms": [
|
||||
"compforms"
|
||||
],
|
||||
"components": [
|
||||
"components"
|
||||
],
|
||||
"utils": [
|
||||
"utils"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./src/**/**/*"
|
||||
|
@@ -3,6 +3,16 @@ const path = require('path');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
|
||||
const lessLoader = {
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
lessOptions: {
|
||||
javascriptEnabled: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
@@ -14,6 +24,7 @@ module.exports = {
|
||||
plugins: [
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new CleanWebpackPlugin(),
|
||||
new ReactRefreshWebpackPlugin(),
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'Output Management',
|
||||
template: './src/index.html'
|
||||
@@ -70,11 +81,11 @@ module.exports = {
|
||||
loader: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
/* {
|
||||
test: /\.css$/,
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
use: [ 'style-loader', 'css-loader' ]
|
||||
},
|
||||
}, */
|
||||
{
|
||||
test: /\.(png|svg|jpg|gif)$/,
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
@@ -84,6 +95,22 @@ module.exports = {
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/,
|
||||
include: path.resolve(__dirname, 'src'),
|
||||
use: [ 'file-loader' ]
|
||||
},
|
||||
{
|
||||
test: /\.theme\.(less|css)$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
options: { injectType: 'lazyStyleTag' }
|
||||
},
|
||||
'css-loader',
|
||||
lessLoader
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.(less|css)$/,
|
||||
exclude: /\.theme\.(less|css)$/i,
|
||||
use: [ 'style-loader', 'css-loader' ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user