client: migrate some styles to CSS module or CSS-in-JS

This commit is contained in:
Jesse Chan
2021-11-08 21:27:51 -08:00
parent 2063c820f1
commit ffcc5c8e05
24 changed files with 1520 additions and 151 deletions

View File

@@ -23,8 +23,31 @@ module.exports = {
},
],
},
{
test: /\.module\.s?css$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: true,
modules: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
{
test: /\.s?css$/,
exclude: /\.module\.s?css$/,
use: [
{
loader: 'style-loader',
@@ -84,10 +107,11 @@ module.exports = {
},
entry: paths.appIndex,
resolve: {
extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
extensions: ['.cjs', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.scss', '.json'],
alias: {
'@client': path.resolve('./client/src/javascript'),
'@shared': path.resolve('./shared'),
'@styles': path.resolve('./client/src/sass/modules'),
},
},
output: {

View File

@@ -108,6 +108,7 @@ module.exports = {
alias: {
'@client': path.resolve('./client/src/javascript'),
'@shared': path.resolve('./shared'),
'@styles': path.resolve('./client/src/sass/modules'),
},
},
output: {

View File

@@ -14,6 +14,8 @@ import WindowTitle from './general/WindowTitle';
import LoadingOverlay from './general/LoadingOverlay';
import LogoutButton from './sidebar/LogoutButton';
import AppWrapperStyles from '@styles/app-wrapper.module.scss';
interface AppWrapperProps {
children: ReactNode;
className?: string;
@@ -42,8 +44,8 @@ const AppWrapper: FC<AppWrapperProps> = observer((props: AppWrapperProps) => {
if (AuthStore.isAuthenticated && !ClientStatusStore.isConnected && ConfigStore.authMethod !== 'none') {
overlay = (
<div className="application__loading-overlay">
<div className="application__entry-barrier">
<div className={AppWrapperStyles['loading-overlay']}>
<div className={AppWrapperStyles['entry-barrier']}>
<LogoutButton className={css({position: 'absolute', left: '5px', top: '5px'})} />
<ClientConnectionInterruption />
</div>
@@ -56,7 +58,7 @@ const AppWrapper: FC<AppWrapperProps> = observer((props: AppWrapperProps) => {
<WindowTitle />
<TransitionGroup>
{overlay != null ? (
<CSSTransition timeout={{enter: 1000, exit: 1000}} classNames="application__loading-overlay">
<CSSTransition timeout={{enter: 1000, exit: 1000}} classNames={AppWrapperStyles['loading-overlay']}>
{overlay}
</CSSTransition>
) : null}

View File

@@ -10,6 +10,8 @@ import {AccessLevel} from '@shared/schema/constants/Auth';
import type {Credentials} from '@shared/schema/Auth';
import type {ClientConnectionSettings} from '@shared/schema/ClientConnectionSettings';
import AppWrapperStyles from '@styles/app-wrapper.module.scss';
import ClientConnectionSettingsForm from '../general/connection-settings/ClientConnectionSettingsForm';
type LoginFormData = Pick<Credentials, 'username' | 'password'>;
@@ -29,7 +31,7 @@ const AuthForm: FC<AuthFormProps> = ({mode}: AuthFormProps) => {
const isLoginMode = mode === 'login';
return (
<div className="application__entry-barrier">
<div className={AppWrapperStyles['loading-overlay']}>
<Panel spacing="large">
<Form
onSubmit={(submission) => {

View File

@@ -6,6 +6,8 @@ import {CheckmarkThick} from '@client/ui/icons';
import type {Dependencies} from '@client/stores/UIStore';
import AppWrapperStyles from '@styles/app-wrapper.module.scss';
import LoadingIndicator from './LoadingIndicator';
const ICONS = {
@@ -41,7 +43,7 @@ const LoadingOverlay: FC<{dependencies?: Dependencies}> = (props: {dependencies?
const {dependencies} = props;
return (
<div className="application__loading-overlay">
<div className={AppWrapperStyles['loading-overlay']}>
<LoadingIndicator inverse />
{dependencies != null ? <LoadingDependencyList dependencies={dependencies} /> : null}
</div>

View File

@@ -1,3 +1,4 @@
import {css} from '@emotion/css';
import {FC, ReactNode, useRef, useState} from 'react';
import {AddMini, RemoveMini} from '@client/ui/icons';
@@ -54,7 +55,12 @@ const TextboxRepeater: FC<TextboxRepeaterProps> = ({defaultValues, id, label, pl
defaultValue={textbox.value}
label={index === 0 && label}
placeholder={placeholder}
wrapperClassName="textbox-repeater"
wrapperClassName={css({
'.icon': {
height: '12px',
width: '12px',
},
})}
>
<FormElementAddon
onClick={() => {

View File

@@ -7,3 +7,8 @@ declare module '*.md' {
declare module '@lingui/loader!*.json?raw-lingui' {
export const messages: Record<string, string[]>;
}
declare module '*.module.scss' {
const classes: { [key: string]: string };
export default classes;
}

View File

@@ -1,13 +0,0 @@
import classnames from 'classnames';
import {FC, ReactNode} from 'react';
interface ContainerProps {
children: ReactNode;
}
const Container: FC<ContainerProps> = ({children}: ContainerProps) => {
const classes = classnames('container');
return <div className={classes}>{children}</div>;
};
export default Container;

View File

@@ -1,4 +1,4 @@
import classnames from 'classnames';
import {rgba} from 'polished';
import {FC, MouseEvent, ReactNode} from 'react';
export interface OverlayProps {
@@ -17,18 +17,32 @@ const Overlay: FC<OverlayProps> = ({
onContextMenu,
isInteractive,
isTransparent,
}: OverlayProps) => {
const classes = classnames('overlay', additionalClassNames, {
'overlay--no-interaction': !isInteractive,
'overlay--transparent': isTransparent,
});
return (
<div className={classes} onClickCapture={onClick} onContextMenuCapture={onContextMenu}>
{children}
</div>
);
};
}: OverlayProps) => (
<div
css={[
{
background: rgba('#1d2938', 0.95),
bottom: 0,
left: 0,
position: 'fixed',
right: 0,
top: 0,
zIndex: 100,
},
isInteractive || {
pointerEvents: 'none',
},
isTransparent && {
background: 'transparent',
},
additionalClassNames,
]}
onClickCapture={onClick}
onContextMenuCapture={onContextMenu}
>
{children}
</div>
);
Overlay.defaultProps = {
additionalClassNames: undefined,

View File

@@ -1,6 +1,5 @@
export {default as Button} from './components/Button';
export {default as Checkbox} from './components/Checkbox';
export {default as Container} from './components/Container';
export {default as ContextMenu} from './components/ContextMenu';
export {default as Form} from './components/Form';
export {default as FormElementAddon} from './components/FormElementAddon';

View File

@@ -6,11 +6,6 @@ body {
overflow: hidden;
}
.container {
height: 100%;
width: 100%;
}
#app,
.application,
.application__view {

View File

@@ -1,33 +0,0 @@
@use '../tools/colors';
.application {
&__loading-overlay {
align-items: center;
background: colors.$light-blue;
display: flex;
flex-direction: column;
font-size: 0.8em;
height: 100%;
justify-content: center;
left: 0;
opacity: 1;
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
&-exit {
opacity: 1;
transition: opacity 1s;
&-active {
opacity: 0;
}
}
}
&__entry-barrier {
max-width: 500px;
width: 100%;
}
}

View File

@@ -1,6 +0,0 @@
.textbox-repeater {
.icon {
height: 12px;
width: 12px;
}
}

View File

@@ -1,22 +0,0 @@
@use '../tools/colors';
$transfer-data--download: colors.$green;
$transfer-data--upload: colors.$blue;
.transfer-data {
&--download {
color: $transfer-data--download;
.icon {
fill: $transfer-data--download;
}
}
&--upload {
color: $transfer-data--upload;
.icon {
fill: $transfer-data--upload;
}
}
}

View File

@@ -0,0 +1,31 @@
@use '../tools/colors';
.loading-overlay {
align-items: center;
background: colors.$light-blue;
display: flex;
flex-direction: column;
font-size: 0.8em;
height: 100%;
justify-content: center;
left: 0;
opacity: 1;
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
&-exit {
opacity: 1;
transition: opacity 1s;
&-active {
opacity: 0;
}
}
}
.entry-barrier {
max-width: 500px;
width: 100%;
}

View File

@@ -14,7 +14,6 @@
@import 'base/typography';
@import 'components/action-bar';
@import 'components/app-wrapper';
@import 'components/alerts';
@import 'components/attached-panel';
@import 'components/badge';
@@ -44,13 +43,11 @@
@import 'components/sortable-list';
@import 'components/table';
@import 'components/tags';
@import 'components/textbox-repeater';
@import 'components/toolbar';
@import 'components/tooltip';
@import 'components/torrent-details-panel';
@import 'components/torrents';
@import 'components/torrent';
@import 'components/transfer-data';
@import 'views/login';
@import 'views/feeds';

View File

@@ -39,7 +39,6 @@ $grey--harder: harden($grey, 1);
$dark-grey: #34516c;
$dark-grey--light: color.adjust($dark-grey, $lightness: 7%);
$darker-grey: #1d2938;
$darkest-grey: #28303b;
$darkest-grey--hard: #293341;
$darkest-grey--darker: #202d3c;

View File

@@ -1,3 +0,0 @@
.container {
max-width: 900px;
}

View File

@@ -1,19 +0,0 @@
@use '../../tools/colors';
.overlay {
background: rgba(colors.$darker-grey, 0.95);
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: 0;
z-index: 100;
&--transparent {
background: transparent;
}
&--no-interaction {
pointer-events: none;
}
}

View File

@@ -1,20 +0,0 @@
@use '../../tools/colors';
@use '../config/spacing.scss';
.section {
margin-bottom: spacing.$spacing--xxx-large;
&__heading {
margin-bottom: spacing.$spacing;
margin-top: 0;
}
&.inverse {
background: colors.$another-grey;
}
&.padded {
padding: spacing.$spacing--large spacing.$spacing--x-large;
}
}

View File

@@ -8,14 +8,11 @@
@import './components/button';
@import './components/context-menu';
@import './components/container';
@import './components/error';
@import './components/form';
@import './components/element-addon';
@import './components/icon';
@import './components/input';
@import './components/overlay';
@import './components/panel';
@import './components/portal';
@import './components/section';
@import './components/select';

1408
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -209,6 +209,7 @@
"tsconfig-paths": "^3.11.0",
"typed-emitter": "^1.4.0",
"typescript": "~4.4.4",
"typescript-plugin-css-modules": "^3.4.0",
"use-query-params": "^1.2.3",
"webpack": "^5.61.0",
"webpack-dev-server": "^4.4.0",

View File

@@ -19,8 +19,10 @@
"paths": {
"@client/*": ["client/src/javascript/*"],
"@server/*": ["server/*"],
"@shared/*": ["shared/*"]
}
"@shared/*": ["shared/*"],
"@styles/*": ["client/src/sass/modules/*"]
},
"plugins": [{"name": "typescript-plugin-css-modules"}]
},
"include": ["./client/**/*.ts", "./client/**/*.tsx", "./server/**/*.ts", "./server/**/*.tsx"],
"exclude": ["node_modules"]