Deduplicate CSS

Sass and webpack don't interact well together. A (non-partial) SCSS file
compiled in Sass context and imported from a component will duplicate all its
internal imports.

Create a single SCSS/CSS stylesheet which is not duplicated multiple times due
to per-component import. The fling.scss master style sheet is now imported once
in index.js and contains styles for all components.

Signed-off-by: Armin Friedl <dev@friedl.net>
This commit is contained in:
Armin Friedl 2020-05-17 16:28:33 +02:00
parent eb407f90b6
commit 70cd9c29be
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
29 changed files with 281 additions and 271 deletions

View file

@ -1,8 +1,6 @@
import React, {useState} from 'react'; import React, {useState} from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import './Error.scss';
import log from 'loglevel'; import log from 'loglevel';
export default (props) => { export default (props) => {

View file

@ -7,8 +7,6 @@ import FlingContent from './FlingContent';
import {HashRouter} from 'react-router-dom'; import {HashRouter} from 'react-router-dom';
import './Fling.scss';
export default function Fling() { export default function Fling() {
const [activeFling, setActiveFling] = useState(undefined); const [activeFling, setActiveFling] = useState(undefined);

View file

@ -5,8 +5,6 @@ import classNames from 'classnames';
import {artifactClient} from '../util/flingclient'; import {artifactClient} from '../util/flingclient';
import './FlingArtifacts.scss';
function FlingArtifactControl(props) { function FlingArtifactControl(props) {
return( return(
<div className={`btn-group ${props.hidden ? "d-invisible": "d-visible"}`}> <div className={`btn-group ${props.hidden ? "d-invisible": "d-visible"}`}>

View file

@ -1,34 +0,0 @@
@import "../styles/base.scss";
@import "~spectre.css/src/_variables.scss";
.table {
table-layout: fixed;
}
thead th {
&:nth-child(1) {
width:60%;
}
&:nth-child(2) {
width:20%;
}
&:nth-child(3) {
width:20%;
}
// control box
&:nth-child(4) {
text-align: right;
width: 4*$control-size;
}
}
tbody td {
&:nth-child(4) {
text-align: right;
width: 4*$control-size;
}
}
.artifact-row:hover {
background-color: $secondary-color;
}

View file

@ -10,8 +10,6 @@ import FlingArtifacts from './FlingArtifacts';
import Upload from './Upload'; import Upload from './Upload';
import Settings from './Settings'; import Settings from './Settings';
import './FlingContent.scss';
export default function FlingContent(props) { export default function FlingContent(props) {
let location = useLocation(); let location = useLocation();

View file

@ -1,6 +0,0 @@
@import "../styles/base.scss";
.fling-content {
@extend %shadow;
background-color: #ffffff;
}

View file

@ -5,8 +5,6 @@ import {flingClient} from '../util/flingclient';
import FlingTile from './FlingTile'; import FlingTile from './FlingTile';
import './FlingList.scss';
export default function FlingList(props) { export default function FlingList(props) {
const [flings, setFlings] = useState([]); const [flings, setFlings] = useState([]);
useEffect(() => { refreshFlingList(); }, [props.activeFling]); useEffect(() => { refreshFlingList(); }, [props.activeFling]);

View file

@ -1,12 +0,0 @@
@import "../styles/base.scss";
@import "~spectre.css/src/_variables.scss";
.panel {
background-color: $light-color;
border: none;
@extend %shadow;
}
.panel .panel-body {
overflow-y: visible;
}

View file

@ -4,8 +4,6 @@ import classNames from 'classnames';
import {flingClient} from '../util/flingclient'; import {flingClient} from '../util/flingclient';
import './FlingTile.scss';
function TileAction(props) { function TileAction(props) {
let shareUrlRef = useRef(null); let shareUrlRef = useRef(null);

View file

@ -1,24 +0,0 @@
@import "../styles/base.scss";
@import "~spectre.css/src/_variables.scss";
.tile.active {
background-color: $gray-color-light;
}
.tile:hover {
background-color: $secondary-color;
}
.divider {
transform: scale(0.8, 1) translate(-10%,0);
}
.input-group-addon.input-group-addon-sm {
padding-top: 0.05rem;
padding-bottom: 0.05rem;
}
.form-input.input-share-id {
cursor: text;
box-shadow: none;
}

View file

@ -4,8 +4,6 @@ import {useHistory, useLocation} from 'react-router-dom';
import request, {setAuth} from '../util/request'; import request, {setAuth} from '../util/request';
import './Login.scss';
import Error from './Error'; import Error from './Error';
export default () => { export default () => {
@ -22,7 +20,7 @@ export default () => {
<div className="container-center"> <div className="container-center">
<div> <div>
<Error errors={errors} clearErrors={clearErrors} > <Error errors={errors} clearErrors={clearErrors} >
<form className="container-login" onSubmit={handleSubmit}> <form className="login-form" onSubmit={handleSubmit}>
<div className="form-group"> <div className="form-group">
<label className="form-label" htmlFor="username">Username</label> <label className="form-label" htmlFor="username">Username</label>
<input className="form-input" id="username" name="username" type="text" placeholder="Username" <input className="form-input" id="username" name="username" type="text" placeholder="Username"
@ -33,7 +31,7 @@ export default () => {
<input className="form-input" id="password" name="password" type="password" placeholder={"*".repeat(18)} <input className="form-input" id="password" name="password" type="password" placeholder={"*".repeat(18)}
value={password} onChange={handleChange} /> value={password} onChange={handleChange} />
</div> </div>
<div className="form-action-row"> <div className="login-action-row">
<div className="form-group"> <div className="form-group">
<label className="form-switch input-sm"> <label className="form-switch input-sm">
<input type="checkbox" /> <input type="checkbox" />
@ -45,7 +43,7 @@ export default () => {
</form> </form>
</Error> </Error>
<p className="bottom-text">Ready. Set. Fling.</p> <p className="login-footer">Ready. Set. Fling.</p>
</div> </div>
</div> </div>

View file

@ -1,19 +0,0 @@
@import "../styles/base.scss";
.container-login {
@extend %shadow;
padding: 1rem;
border-radius: 0.25rem;
}
.bottom-text {
text-align: center;
font-size: 0.75rem;
color: $light-grey;
margin-top: 0.25rem;
}
.form-action-row {
display: flex;
justify-content: space-between;
}

View file

@ -3,7 +3,6 @@ import React from 'react';
import request from '../util/request'; import request from '../util/request';
import './Navbar.scss';
import send from './send.svg'; import send from './send.svg';
export default function Navbar() { export default function Navbar() {

View file

@ -1,46 +0,0 @@
$primary-color: rgb(235, 236, 237);
@import "../styles/base.scss";
@import "~spectre.css/src/_mixins.scss";
@import "~spectre.css/src/_variables.scss";
@import "~spectre.css/src/_buttons.scss";
.navbar {
@extend %shadow;
padding: 0.2rem;
background-color: #323334;
color: #ebeced;
}
.navbar .navbar-brand {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
color: $primary-color;
}
.navbar input {
background-color: #464748;
border: none;
}
.navbar input + button.input-group-btn {
background-color: #464748;
border: none;
border-left: solid;
border-left-color: #323334;
border-left-width: thin;
}
.navbar-brand img {
height: 1.1rem;
margin-right: 0.3rem;
margin-left: 0.2rem;
}
.navbar-control {
.btn, a {
font-size: 0.7rem;
}
}

View file

@ -5,8 +5,6 @@ import classNames from 'classnames';
import {flingClient} from '../util/flingclient'; import {flingClient} from '../util/flingclient';
import './Settings.scss';
export default function Settings(props) { export default function Settings(props) {
let [fling, setFling] = useState({name: "", sharing: {directDownload: false, allowUpload: true, shared: true, shareUrl: ""}}); let [fling, setFling] = useState({name: "", sharing: {directDownload: false, allowUpload: true, shared: true, shareUrl: ""}});
let [shareUrlUnique, setShareUrlUnique] = useState(true); let [shareUrlUnique, setShareUrlUnique] = useState(true);

View file

@ -7,7 +7,6 @@ import {artifactClient} from '../util/flingclient';
import upload from './upload.svg'; import upload from './upload.svg';
import drop from './drop.svg'; import drop from './drop.svg';
import './Upload.scss';
export default function Upload(props) { export default function Upload(props) {

View file

@ -1,54 +0,0 @@
@import "../styles/base.scss";
@import "~spectre.css/src/mixins/_clearfix.scss";
@import "~spectre.css/src/mixins/_position.scss";
@import "~spectre.css/src/_variables.scss";
@import "~spectre.css/src/_layout.scss";
@import "~spectre.css/src/utilities/_position.scss";
.dropzone {
@extend %shadow;
position: relative;
text-align: center;
}
.dropzone-icon {
height: 64px;
width: 64px;
}
.dropzone-icon-upload {
@extend .dropzone-icon;
opacity: 0.3;
}
.file-list {
@extend %shadow;
display: flex;
flex-direction: column;
height: 100%;
}
.file-list .row {
@extend .container;
@extend .my-2;
flex: 1;
display: flex;
flex-direction: column;
}
.total-upload {
float: left;
align-self: flex-start;
}
.btn-upload {
align-self: flex-end;
}
.my-input {
inset: 0;
position: absolute;
width: 100%;
height: 100%;
z-index: 0;
}

View file

@ -1,30 +1,43 @@
%shadow-xs { /***********\
| Shadows |
\***********/
@mixin shadow-xs {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05); box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
} }
%shadow-sm {
@mixin shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
} }
%shadow {
@mixin shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
} }
%shadow-md {
@mixin shadow-md {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
} }
%shadow-lg {
@mixin shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
} }
%shadow-xl {
@mixin shadow-xl {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
} }
%shadow-2xl {
@mixin shadow-2xl {
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
} }
%shadow-inner {
@mixin shadow-inner {
box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06); box-shadow: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06);
} }
%shadow-outline {
@mixin shadow-outline {
box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5); box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
} }
%shadow-none {
@mixin shadow-none {
box-shadow: none; box-shadow: none;
} }

View file

@ -1,4 +1,4 @@
/* Center within viewport */ // A flex container that centers it's children within the viewport
.container-center { .container-center {
display: flex; display: flex;
justify-content: center; justify-content: center;

View file

@ -0,0 +1,37 @@
/**********\
| Colors |
\**********/
// Primary colors
$blue-grey: #718096;
$grey: #cbd5e0;
$light-grey: #ebeced;
$dark-grey: #464748;
$red: #e53e3e;
$black: #323334;
// Semantic colors
$primary-color: #4693d2;
$canvas-base-color: $grey;
$navbar-base-color: $black;
$navbar-color: $light-grey;
/*********\
| Fonts |
\*********/
// Sizes
$font-size-base: 1rem; // Assumes the browser default, typically `16px`
$font-size-lg: $font-size-base * 1.25;
$font-size-sm: $font-size-base * .875;
$font-size-xs: $font-size-base * .75;
/**************\
| Breakpoints |
\**************/
$sm: '640px';
$md: '768px';
$lg: '1024px';
$xl: '1280px';

View file

@ -0,0 +1,217 @@
@import "variables";
@import "mixins";
@import "utils";
@import "~spectre.css/src/spectre.scss";
@import "~spectre.css/src/spectre-icons.scss";
@import "~spectre.css/src/spectre-exp.scss";
body {
background-color: $canvas-base-color;
}
/*********\
| Login |
\*********/
// Basic styling for the login form
.login-form {
@include shadow;
padding: 1rem;
border-radius: 0.25rem;
background-color: #ffffff;
}
// Row of actions - submit, remember me
.login-action-row {
display: flex;
justify-content: space-between;
}
// Little grey text at the bottom of the login box
.login-footer {
text-align: center;
font-size: 0.75rem;
color: $light-grey;
margin-top: 0.25rem;
}
/*********\
| Navbar |
\*********/
.navbar {
@include shadow;
padding: 0.4rem;
background-color: $navbar-base-color;
color: $navbar-color;
.navbar-brand {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
color: $navbar-color;
img {
height: 1.1rem;
margin-right: 0.3rem;
margin-left: 0.2rem;
}
}
input {
background-color: $dark-grey;
border: none;
}
.btn.btn-link {
color: $navbar-color;
}
input + button.input-group-btn {
background-color: $dark-grey;
border: none;
border-left: solid;
border-left-color: $navbar-base-color;
border-left-width: thin;
}
}
.navbar-control {
.btn, a {
font-size: 0.7rem;
}
}
/*************\
| FlingList |
\*************/
.panel {
background-color: #ffffff;
}
/*************\
| FlingTile |
\*************/
.divider {
transform: scale(0.8, 1) translate(-10%,0);
}
.tile{
&.active {
background-color: $gray-color-light;
}
&:hover {
background-color: $secondary-color;
}
}
.input-group-addon.input-group-addon-sm {
padding-top: 0.05rem;
padding-bottom: 0.05rem;
}
.form-input.input-share-id {
cursor: text;
box-shadow: none;
}
/****************\
| FlingContent |
\****************/
.fling-content {
@include shadow;
background-color: #ffffff;
}
/******************\
| FlingArtifacts |
\******************/
.table {
table-layout: fixed;
}
thead th {
&:nth-child(1) {
width:60%;
}
&:nth-child(2) {
width:20%;
}
&:nth-child(3) {
width:20%;
}
// control box
&:nth-child(4) {
text-align: right;
width: 4*$control-size;
}
}
tbody td {
&:nth-child(4) {
text-align: right;
width: 4*$control-size;
}
}
.artifact-row:hover {
background-color: $secondary-color;
}
/**********\
| Upload |
\**********/
.dropzone {
@include shadow;
position: relative;
text-align: center;
}
.dropzone-icon {
height: 64px;
width: 64px;
}
.dropzone-icon-upload {
@extend .dropzone-icon;
opacity: 0.3;
}
.file-list {
@include shadow;
display: flex;
flex-direction: column;
height: 100%;
}
.file-list .row {
@extend .container;
@extend .my-2;
flex: 1;
display: flex;
flex-direction: column;
}
.total-upload {
float: left;
align-self: flex-start;
}
.btn-upload {
align-self: flex-end;
}
.my-input {
inset: 0;
position: absolute;
width: 100%;
height: 100%;
z-index: 0;
}

View file

@ -1,15 +0,0 @@
@import "~normalize.css/normalize.css";
@import "~spectre.css/dist/spectre.min.css";
@import "~spectre.css/dist/spectre-icons.css";
@import "partials/_shadows.scss";
@import "variables/_breakpoints.scss";
@import "variables/_colors.scss";
@import "form.scss";
@import "container.scss";
body {
background-color: #ebeced;
}

View file

@ -1,17 +0,0 @@
@import "variables/_colors.scss";
@import "variables/_sizes.scss";
@import "partials/_shadows.scss";
.form-input {
@extend %shadow;
appearance: none;
}
.form-input-error {
border-color: $red;
}
.form-error-text {
color: $red;
font-size: $font-size-xs;
}

View file

@ -1,4 +0,0 @@
$sm: '640px';
$md: '768px';
$lg: '1024px';
$xl: '1280px';

View file

@ -1,4 +0,0 @@
$grey: #718096;
$light-grey: #cbd5e0;
$red: #e53e3e;

View file

@ -1,4 +0,0 @@
$font-size-base: 1rem; // Assumes the browser default, typically `16px`
$font-size-lg: $font-size-base * 1.25;
$font-size-sm: $font-size-base * .875;
$font-size-xs: $font-size-base * .75;