Add experimental high contrast mode (#1845)

Add an experimental high contrast color theme to SCM-Manager. The high contrast mode uses a dark background and color with a high contrast for a better accessibility.
The change adds the theme to ui-styles and theme switcher to the storybook of ui-components.
This commit is contained in:
Sebastian Sdorra
2021-11-04 08:33:11 +01:00
committed by GitHub
parent 07f0b22031
commit 0efdd2a483
53 changed files with 60796 additions and 60183 deletions

View File

@@ -0,0 +1,2 @@
- type: added
descripion: Experimental high contrast mode ([#1845](https://github.com/scm-manager/scm-manager/pull/1845))

View File

@@ -0,0 +1,46 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
const HtmlWebpackPlugin = require('html-webpack-plugin');
class RemoveThemesPlugin {
apply (compiler) {
compiler.hooks.compilation.tap('RemoveThemesPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeAssetTagGeneration.tapAsync(
'RemoveThemesPlugin',
(data, cb) => {
if (data.assets.css) {
data.assets.css = data.assets.css.filter(css => css.startsWith("ui-theme-"))
}
data.assets.css = [];
// Tell webpack to move on
cb(null, data)
}
)
})
}
}
module.exports = RemoveThemesPlugin

View File

@@ -0,0 +1,80 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
const path = require("path");
const fs = require("fs");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const RemoveThemesPlugin = require("./RemoveThemesPlugin");
const WorkerPlugin = require("worker-plugin");
const root = path.resolve("..");
const themedir = path.join(root, "ui-styles", "src");
const themes = fs
.readdirSync(themedir)
.map((filename) => path.parse(filename))
.filter((p) => p.ext === ".scss")
.reduce((entries, current) => ({ ...entries, [current.name]: path.join(themedir, current.base) }), {});
module.exports = {
stories: ["../src/**/*.stories.tsx"],
addons: ["storybook-addon-i18next", "storybook-addon-themes"],
webpackFinal: async (config) => {
// add our themes to webpack entry points
config.entry = {
main: config.entry,
...themes,
};
// fix usage of web workers
// required for diff with syntax highlighting
config.plugins.push(new WorkerPlugin());
// create separate css files for our themes
config.plugins.push(
new MiniCssExtractPlugin({
filename: "ui-theme-[name].css",
ignoreOrder: false
}),
);
config.module.rules.push({
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader",
],
});
// the html-webpack-plugin adds the generated css to iframe,
// which overrides our manually loaded css files.
// So we use a custom plugin which uses a hook of html-webpack-plugin
// to filter our themes from the output.
config.plugins.push(new RemoveThemesPlugin());
return config;
},
};

View File

@@ -0,0 +1,26 @@
<!--
MIT License
Copyright (c) 2020-present Cloudogu GmbH and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<link id="ui-theme" data-theme="light" rel="stylesheet" type="text/css" href="/ui-theme-light.css">

View File

@@ -23,11 +23,10 @@
*/
import i18next from "i18next";
import { initReactI18next } from "react-i18next";
import { addDecorator, configure } from "@storybook/react";
import { withI18next } from "storybook-addon-i18next";
import "!style-loader!css-loader!sass-loader!../../ui-styles/src/scm.scss";
import React from "react";
import React, {useEffect} from "react";
import withApiProvider from "./withApiProvider";
import { withThemes } from 'storybook-addon-themes/react';
let i18n = i18next;
@@ -43,30 +42,52 @@ i18n.use(initReactI18next).init({
lng: "en",
fallbackLng: "en",
interpolation: {
escapeValue: false
escapeValue: false,
},
react: {
useSuspense: false
useSuspense: false,
},
backend: {
loadPath: "/locales/{{lng}}/{{ns}}.json",
init: {
credentials: "same-origin"
}
}
credentials: "same-origin",
},
},
});
addDecorator(
export const decorators = [
withI18next({
i18n,
languages: {
en: "English",
de: "Deutsch",
es: "Spanisch"
es: "Spanisch",
},
}),
withApiProvider,
withThemes
];
const Decorator = ({children, themeName}) => {
useEffect(() => {
const link = document.querySelector("#ui-theme");
if (link && link["data-theme"] !== themeName) {
link.href = `/ui-theme-${themeName}.css`;
link["data-theme"] = themeName;
}
})
);
}, [themeName]);
return <>{children}</>
};
addDecorator(withApiProvider);
configure(require.context("../src", true, /\.stories\.tsx?$/), module);
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
themes: {
Decorator,
clearable: false,
default: "light",
list: [
{ name: "light", color: "#fff" },
{ name: "highcontrast", color: "#000" },
],
},
};

View File

@@ -26,9 +26,9 @@
"@scm-manager/prettier-config": "^2.10.1",
"@scm-manager/tsconfig": "^2.12.0",
"@scm-manager/ui-tests": "^2.25.1-SNAPSHOT",
"@storybook/addon-actions": "^6.1.17",
"@storybook/addon-storyshots": "^6.1.17",
"@storybook/react": "^6.1.17",
"@storybook/addon-actions": "^6.3.12",
"@storybook/addon-storyshots": "^6.3.12",
"@storybook/react": "^6.3.12",
"@types/classnames": "^2.2.9",
"@types/css": "^0.0.31",
"@types/enzyme": "^3.10.3",
@@ -52,9 +52,11 @@
"fetch-mock": "^7.5.1",
"gitdiff-parser": "^0.1.2",
"i18next-fetch-backend": "^2.2.0",
"mini-css-extract-plugin": "^1.6.2",
"raf": "^3.4.0",
"react-test-renderer": "^17.0.1",
"storybook-addon-i18next": "^1.3.0",
"storybook-addon-themes": "^6.1.0",
"to-camel-case": "^1.0.0",
"worker-plugin": "^3.2.0"
},

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { ReactNode } from "react";
import React, { ReactElement, ReactNode } from "react";
import Button from "./Button";
import { storiesOf } from "@storybook/react";
import styled from "styled-components";
@@ -31,7 +31,6 @@ import DeleteButton from "./DeleteButton";
import DownloadButton from "./DownloadButton";
import EditButton from "./EditButton";
import SubmitButton from "./SubmitButton";
import { ReactElement } from "react";
import { MemoryRouter } from "react-router-dom";
const colors = ["primary", "link", "info", "success", "warning", "danger", "white", "light", "dark", "black", "text"];
@@ -43,7 +42,7 @@ const Spacing = styled.div`
const SpacingDecorator = (story: () => ReactNode) => <Spacing>{story()}</Spacing>;
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
storiesOf("Buttons|Button", module)
storiesOf("Buttons/Button", module)
.addDecorator(RoutingDecorator)
.add("Colors", () => (
<div>
@@ -72,7 +71,7 @@ storiesOf("Buttons|Button", module)
));
const buttonStory = (name: string, storyFn: () => ReactElement) => {
return storiesOf("Buttons|" + name, module)
return storiesOf("Buttons/" + name, module)
.addDecorator(RoutingDecorator)
.addDecorator(SpacingDecorator)
.add("Default", storyFn);

View File

@@ -26,7 +26,7 @@ import { MemoryRouter } from "react-router-dom";
import { storiesOf } from "@storybook/react";
import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
storiesOf("Forms|AddKeyValueEntryToTableField", module)
storiesOf("Forms/AddKeyValueEntryToTableField", module)
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.add("Default", () => (
<div className="m-6">

View File

@@ -105,7 +105,7 @@ const LegacyEvents: FC = () => {
);
};
storiesOf("Forms|Checkbox", module)
storiesOf("Forms/Checkbox", module)
.addDecorator(storyFn => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("Default", () => (
<Spacing>

View File

@@ -25,7 +25,7 @@ import React from "react";
import { storiesOf } from "@storybook/react";
import DropDown from "./DropDown";
storiesOf("Forms|DropDown", module)
storiesOf("Forms/DropDown", module)
.add("Default", () => (
<DropDown
options={["en", "de", "es"]}

View File

@@ -63,7 +63,7 @@ const ReactHookForm: FC = () => {
);
};
storiesOf("Forms|FileInput", module)
storiesOf("Forms/FileInput", module)
.addDecorator((storyFn) => <Decorator>{storyFn()}</Decorator>)
.addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("Default", () => <ReactHookForm />);

View File

@@ -127,7 +127,7 @@ const LegacyEvents: FC = () => {
);
};
storiesOf("Forms|InputField", module)
storiesOf("Forms/InputField", module)
.addDecorator((storyFn) => <Decorator>{storyFn()}</Decorator>)
.addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("AutoFocus", () => <InputField label="Field with AutoFocus" autofocus={true} />)

View File

@@ -112,7 +112,7 @@ const LegacyEvents: FC = () => {
);
};
storiesOf("Forms|Radio", module)
storiesOf("Forms/Radio", module)
.addDecorator(storyFn => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("Default", () => (
<Spacing>

View File

@@ -198,7 +198,7 @@ const PreselectOption: FC = () => {
);
};
storiesOf("Forms|Select", module)
storiesOf("Forms/Select", module)
.addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("Add no existing value", () => <AddNoExistingValue />)
.add("Ref", () => <Ref />)

View File

@@ -170,7 +170,7 @@ const LegacyEvents: FC = () => {
);
};
storiesOf("Forms|Textarea", module)
storiesOf("Forms/Textarea", module)
.addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
.add("OnChange", () => <OnChangeTextarea />)
.add("OnSubmit", () => <OnSubmitTextare />)

View File

@@ -64,7 +64,7 @@ const withBinder = (binder: Binder) => {
);
};
storiesOf("Layout|Footer", module)
storiesOf("Footer", module)
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.add("Default", () => {
return <Footer me={trillian} version="2.0.0" links={{}} />;

View File

@@ -44,7 +44,7 @@ const buttons = [
}
];
storiesOf("Modal|ConfirmAlert", module)
storiesOf("Modal/ConfirmAlert", module)
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.add("Default", () => <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttons} />)
.add("WithButton", () => {

View File

@@ -71,7 +71,7 @@ const withFormElementsFooter = (
</ButtonGroup>
);
storiesOf("Modal|Modal", module)
storiesOf("Modal/Modal", module)
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.add("Default", () => (
<NonCloseableModal>

View File

@@ -52,7 +52,7 @@ const withRoute = (route: string) => {
return (story: ReactElement<any>) => <MemoryRouter initialEntries={[route]}>{story}</MemoryRouter>;
};
storiesOf("Navigation|Secondary", module)
storiesOf("Secondary Navigation", module)
.addDecorator(story => <StateMenuContextProvider>{story()}</StateMenuContextProvider>)
.addDecorator(story => (
<Columns className="columns">

View File

@@ -85,7 +85,7 @@ const fileControlFactory: (changeset: Changeset) => FileControlFactory = (change
return links.map(({ url, label }) => <JumpToFileButton tooltip={label} link={url} />);
};
storiesOf("Diff", module)
storiesOf("Repositories/Diff", module)
.addDecorator(RoutingDecorator)
.addDecorator((storyFn) => <Container>{storyFn()}</Container>)
.add("Default", () => <Diff diff={diffFiles} />)

View File

@@ -98,7 +98,7 @@ const archivedExportingRepository = {
"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.",
};
storiesOf("RepositoryEntry", module)
storiesOf("Repositories/RepositoryEntry", module)
.addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.addDecorator((storyFn) => <Container>{storyFn()}</Container>)
.add("Default", () => {

View File

@@ -164,7 +164,7 @@ const Robohash: FC = ({ children }) => {
return <BinderContext.Provider value={binder}>{children}</BinderContext.Provider>;
};
storiesOf("Annotate", module)
storiesOf("Repositories/Annotate", module)
.addDecorator(storyFn => <MemoryRouter initialEntries={["/"]}>{storyFn()}</MemoryRouter>)
.addDecorator(storyFn => <Wrapper className="box">{storyFn()}</Wrapper>)
.add("Default", () => (

View File

@@ -71,7 +71,7 @@ function copy<T>(input: T): T {
return JSON.parse(JSON.stringify(input));
}
storiesOf("Changesets", module)
storiesOf("Repositories/Changesets", module)
.addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
.addDecorator(storyFn => <Wrapper className="box box-link-shadow">{storyFn()}</Wrapper>)
.add("Default", () => <ChangesetRow repository={repository} changeset={three}/>)

View File

@@ -43,10 +43,10 @@
pre[class*="language-"],
code[class*="language-"] {
color: #363636;
color: var(--sh-base-color);
font-size: 1rem;
text-shadow: none;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-family: var(--sh-font-family);
direction: ltr;
text-align: left;
white-space: pre;
@@ -67,7 +67,7 @@ code[class*="language-"]::selection,
pre[class*="language-"]::-moz-selection,
code[class*="language-"]::-moz-selection {
text-shadow: none;
background: #7fe3cd;
background: var(--sh-selected-color);
}
@media print {
@@ -83,14 +83,14 @@ pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
background: #ffffff;
background: var(--sh-block-background);
}
:not(pre) > code[class*="language-"] {
padding: .1em .3em;
border-radius: .3em;
color: #ff3860;
background: #fbe7eb;
color: var(--sh-inline-code-color);
background: var(--sh-inline-code-background);
}
/*********************************************************
@@ -105,11 +105,11 @@ pre[class*="language-"] {
.token.prolog,
.token.doctype,
.token.cdata {
color: #9a9a9a;
color: var(--sh-comment-color);
}
.token.punctuation {
color: #9a9a9a;
color: var(--sh-punctuation-color);
}
.token.property,
@@ -119,7 +119,7 @@ pre[class*="language-"] {
.token.constant,
.token.symbol,
.token.deleted {
color: #2c99c7;
color: var(--sh-property-color);
}
.token.selector,
@@ -128,7 +128,7 @@ pre[class*="language-"] {
.token.char,
.token.builtin,
.token.inserted {
color: #005f9a;
color: var(--sh-selector-color);
}
.token.operator,
@@ -136,23 +136,24 @@ pre[class*="language-"] {
.token.url,
.language-css .token.string,
.style .token.string {
color: #686868;
color: var(--sh-operator-color);
background: var(--sh-operator-bg);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #00a984;
color: var(--sh-keyword-color);
}
.token.function {
color: #ff3860;
color: var(--sh-function-color);
}
.token.regex,
.token.important,
.token.variable {
color: #a74eb2;
color: var(--sh-variable-color);
}
.token.important,
@@ -188,8 +189,8 @@ pre[class*="language-"] > code[class*="language-"] {
right: 0;
padding: inherit 0;
margin-top: 1em;
background: #f5f5f5;
box-shadow: inset 5px 0 0 #99d8f3;
background: var(--sh-highlight-background);
box-shadow: inset 5px 0 0 var(--sh-highlight-accent);
z-index: 0;
pointer-events: none;
line-height: inherit;

View File

@@ -27,10 +27,10 @@
export default {
'pre[class*="language-"]': {
color: "#363636",
color: "var(--sh-base-color)",
fontSize: "1rem",
textShadow: "none",
fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",
fontFamily: "var(--sh-font-family)",
direction: "ltr",
textAlign: "left",
whiteSpace: "pre",
@@ -47,13 +47,13 @@ export default {
padding: "1em",
margin: ".5em 0",
overflow: "auto",
background: "#ffffff"
background: "var(--sh-block-background)",
},
'code[class*="language-"]': {
color: "#363636",
color: "var(--sh-base-color)",
fontSize: "1rem",
textShadow: "none",
fontFamily: "Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace",
fontFamily: "var(--sh-font-family)",
direction: "ltr",
textAlign: "left",
whiteSpace: "pre",
@@ -66,140 +66,145 @@ export default {
WebkitHyphens: "none",
MozHyphens: "none",
msHyphens: "none",
hyphens: "none"
hyphens: "none",
},
'pre[class*="language-"]::selection': {
textShadow: "none",
background: "#7fe3cd"
background: "var(--sh-selected-color)",
},
'code[class*="language-"]::selection': {
textShadow: "none",
background: "#7fe3cd"
background: "var(--sh-selected-color)",
},
'pre[class*="language-"]::-moz-selection': {
textShadow: "none",
background: "#7fe3cd"
background: "var(--sh-selected-color)",
},
'code[class*="language-"]::-moz-selection': {
textShadow: "none",
background: "#7fe3cd"
background: "var(--sh-selected-color)",
},
':not(pre) > code[class*="language-"]': {
padding: ".1em .3em",
borderRadius: ".3em",
color: "#ff3860",
background: "#fbe7eb"
color: "var(--sh-inline-code-color)",
background: "var(--sh-inline-code-background)",
},
".namespace": {
Opacity: ".7"
Opacity: ".7",
},
comment: {
color: "#9a9a9a"
color: "var(--sh-comment-color)",
},
prolog: {
color: "#9a9a9a"
color: "var(--sh-comment-color)",
},
doctype: {
color: "#9a9a9a"
color: "var(--sh-comment-color)",
},
cdata: {
color: "#9a9a9a"
color: "var(--sh-comment-color)",
},
punctuation: {
color: "#9a9a9a"
color: "var(--sh-punctuation-color)",
},
property: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
tag: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
boolean: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
number: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
constant: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
symbol: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
deleted: {
color: "#2c99c7"
color: "var(--sh-property-color)",
},
selector: {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
"attr-name": {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
string: {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
char: {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
builtin: {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
inserted: {
color: "#005f9a"
color: "var(--sh-selector-color)",
},
operator: {
color: "#686868"
color: "var(--sh-operator-color)",
background: "var(--sh-operator-bg)",
},
entity: {
color: "#686868",
cursor: "help"
color: "var(--sh-operator-color)",
background: "var(--sh-operator-bg)",
cursor: "help",
},
url: {
color: "#686868"
color: "var(--sh-operator-color)",
background: "var(--sh-operator-bg)",
},
".language-css .token.string": {
color: "#686868"
color: "var(--sh-operator-color)",
background: "var(--sh-operator-bg)",
},
".style .token.string": {
color: "#686868"
color: "var(--sh-operator-color)",
background: "var(--sh-operator-bg)",
},
atrule: {
color: "#00a984"
color: "var(--sh-keyword-color)",
},
"attr-value": {
color: "#00a984"
color: "var(--sh-keyword-color)",
},
keyword: {
color: "#00a984"
color: "var(--sh-keyword-color)",
},
function: {
color: "#ff3860"
color: "var(--sh-function-color)",
},
regex: {
color: "#a74eb2"
color: "var(--sh-variable-color)",
},
important: {
color: "#a74eb2",
fontWeight: "bold"
color: "var(--sh-variable-color)",
fontWeight: "bold",
},
variable: {
color: "#a74eb2"
color: "var(--sh-variable-color)",
},
bold: {
fontWeight: "bold"
fontWeight: "bold",
},
title: {
fontWeight: "bold"
fontWeight: "bold",
},
italic: {
fontStyle: "italic"
fontStyle: "italic",
},
"pre[data-line]": {
position: "relative"
position: "relative",
},
'pre[class*="language-"] > code[class*="language-"]': {
position: "relative",
zIndex: "1"
zIndex: "1",
},
".line-highlight": {
position: "absolute",
@@ -207,11 +212,11 @@ export default {
right: "0",
padding: "inherit 0",
marginTop: "1em",
background: "#f5f5f5",
boxShadow: "inset 5px 0 0 #99d8f3",
background: "var(--sh-highlight-background)",
boxShadow: "inset 5px 0 0 var(--sh-highlight-accent)",
zIndex: "0",
pointerEvents: "none",
lineHeight: "inherit",
whiteSpace: "pre"
}
whiteSpace: "pre",
},
};

View File

@@ -38,7 +38,7 @@ const StyledTable = styled(Table)`
}
`;
storiesOf("Table|Table", module)
storiesOf("Table", module)
.add("Default", () => (
<Table
data={[

View File

@@ -14,7 +14,7 @@
"babel-loader": "^8.0.6",
"css-loader": "^3.2.0",
"file-loader": "^4.2.0",
"mini-css-extract-plugin": "^0.12.0",
"mini-css-extract-plugin": "^1.6.2",
"mustache": "^3.1.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"react-refresh": "^0.10.0",

View File

@@ -22,6 +22,7 @@
* SOFTWARE.
*/
const path = require("path");
const fs = require("fs");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const WorkerPlugin = require("worker-plugin");
@@ -46,6 +47,13 @@ if (isDevelopment) {
webpackPlugins.push(new ReactRefreshWebpackPlugin());
}
const themedir = path.join(root, "ui-styles", "src");
const themes = fs
.readdirSync(themedir)
.map((filename) => path.parse(filename))
.filter((p) => p.ext === ".scss")
.reduce((entries, current) => ({ ...entries, [current.name]: path.join(themedir, current.base) }), {});
console.log(`build ${mode} bundles`);
module.exports = [
@@ -163,7 +171,7 @@ module.exports = [
{
mode,
context: root,
entry: "./ui-styles/src/scm.scss",
entry: themes,
module: {
rules: [
{
@@ -184,7 +192,7 @@ module.exports = [
},
plugins: [
new MiniCssExtractPlugin({
filename: "ui-styles.css",
filename: "ui-theme-[name].css",
ignoreOrder: false
})
],
@@ -193,7 +201,7 @@ module.exports = [
},
output: {
path: path.join(root, "build", "webapp", "assets"),
filename: "ui-styles.bundle.js"
filename: "ui-theme-[name].bundle.js"
}
},
{

View File

@@ -19,8 +19,10 @@
"@scm-manager/eslint-config": "^2.12.0",
"@scm-manager/prettier-config": "^2.10.1",
"css-loader": "^3.2.0",
"html-webpack-plugin": "4",
"prettier": "^2.1.2",
"sass": "^1.26.3",
"raw-loader": "^4.0.2",
"sass": "^1.43.2",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"webpack": "^4.41.5",

View File

@@ -0,0 +1,84 @@
<!--
MIT License
Copyright (c) 2020-present Cloudogu GmbH and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>SCM-Manager Styleguide</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<meta content="#000000" name="theme-color">
<style>
body {
display: flex;
min-height: 100vh;
align-items: center;
justify-content: center;
text-align: center;
font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
}
h1 {
font-size: 3rem;
}
h2 {
font-size: 2rem;
}
a {
background-color: #00d1df;
border: none;
color: white;
padding: 20px 34px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 20px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
}
</style>
</head>
<body>
<main>
<h1>SCM-Manager Styleguide</h1>
<h2>Choose your Theme</h2>
<div class="themes">
<% _.each(htmlWebpackPlugin.options.themes, function(theme){ %>
<a href="<%= theme %>.html">
<%= theme %>
</a>
<% }); %>
</div>
</main>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,45 @@
<!--
MIT License
Copyright (c) 2020-present Cloudogu GmbH and Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>SCM-Manager Styleguide</title>
<meta content="text/html; charset=UTF-8" http-equiv="content-type">
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<meta content="#000000" name="theme-color">
<link href="ui-webapp/favicon.ico" rel="shortcut icon">
<script data-type="theme" src="/theme-<%= htmlWebpackPlugin.options.theme %>.bundle.js" type="application/javascript"></script>
</head>
<body>
<div class="container mt-5">
<h1 class="title is-1">Styleguide SCM-Manager 2</h1>
<h2 class="subtitle">Theme <%= htmlWebpackPlugin.options.theme %></h2>
</div>
<%= require('raw-loader!./_styleguide.html').default %>
<script data-type="theme" src="js/styleguide.js" type="application/javascript"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,48 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
const rgb2hex = (c) => "#" + c.match(/\d+/g).map((x) => (+x).toString(16).padStart(2, 0)).join``;
function onClickColorButton(e) {
const button = e.target;
const cell = button.parentElement;
const div = cell.querySelector("div.color-text");
if (div) {
div.remove();
} else {
let color = window.getComputedStyle(button).backgroundColor;
color = rgb2hex(color);
const colorText = document.createElement("div");
colorText.className = "color-text";
colorText.innerText = color;
cell.appendChild(colorText);
}
}
document.querySelectorAll("table.colors span.button").forEach((button) => {
button.addEventListener("click", onClickColorButton);
});

File diff suppressed because one or more lines are too long

View File

@@ -21,26 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions";
//$link: #007EB0;
$info: #33b2e8;
$turquoise: #00d1df;
$blue: #33b2e8;
$cyan: $blue;
$green: #00c79b;
$warning: #ffdd57;
$blue-light: #98d8f3;
$danger: #ff3860;
$high-contrast-danger: #e63453;
$family-monospace: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro", monospace;
$high-contrast-background-color: #050514;
$high-contrast-light-gray: #f2f2f2;
.table.high-contrast, .table.high-contrast thead td, .table.high-contrast thead th {
background-color: $high-contrast-background-color;
color: $high-contrast-light-gray;
}
// TODO split into multiple files
.is-ellipsis-overflow {
overflow: hidden;
@@ -89,6 +70,7 @@ hr.header-with-actions {
width: 100%;
}
.input-button {
// TODO color def
border: 2px solid #e9f7fd;
padding: 1em 1em;
margin-top: 0 !important;
@@ -107,84 +89,6 @@ footer.footer {
}
}
// 6. Import the rest of Bulma
@import "bulma/bulma";
@import "bulma-tooltip/dist/css/bulma-tooltip.min";
@import "bulma-popover/css/bulma-popover";
$dark-75: scale-color($dark, $lightness: 25%);
$dark-50: scale-color($dark, $lightness: 50%);
$dark-25: scale-color($dark, $lightness: 75%);
$info-75: scale-color($info, $lightness: 25%);
$info-50: scale-color($info, $lightness: 50%);
$info-25: scale-color($info, $lightness: 75%);
$link-75: scale-color($link, $lightness: 25%);
$link-50: scale-color($link, $lightness: 50%);
$link-25: scale-color($link, $lightness: 75%);
$primary-75: scale-color($primary, $lightness: 25%);
$primary-50: scale-color($primary, $lightness: 50%);
$primary-25: scale-color($primary, $lightness: 75%);
$success-75: scale-color($success, $lightness: 25%);
$success-50: scale-color($success, $lightness: 50%);
$success-25: scale-color($success, $lightness: 75%);
$warning-75: scale-color($warning, $lightness: 25%);
$warning-50: scale-color($warning, $lightness: 50%);
$warning-25: scale-color($warning, $lightness: 75%);
$danger-75: scale-color($danger, $lightness: 25%);
$danger-50: scale-color($danger, $lightness: 50%);
$danger-25: scale-color($danger, $lightness: 75%);
$high-contrast-danger-75: scale-color($high-contrast-danger, $lightness: 25%);
$high-contrast-danger-50: scale-color($high-contrast-danger, $lightness: 50%);
$high-contrast-danger-25: scale-color($high-contrast-danger, $lightness: 75%);
//high-contrast light gray
$light-75: darken($high-contrast-light-gray, 15%);
$light-50: darken($high-contrast-light-gray, 30%);
$light-25: darken($high-contrast-light-gray, 45%);
/*
// not supported by ie
// css vars for external reuse
:root {
// asc sorted initial variables
--black: #{$black};
--white: #{$white};
// asc sorted derived-variables
--primary: #{$primary};
--primary-75: #{$primary-75};
--primary-50: #{$primary-50};
--primary-25: #{$primary-25};
--info: #{$info};
--info-75: #{$info-75};
--info-50: #{$info-50};
--info-25: #{$info-25};
--success: #{$success};
--success-75: #{$success-75};
--success-50: #{$success-50};
--success-25: #{$success-25};
--warning: #{$warning};
--warning-75: #{$warning-75};
--warning-50: #{$warning-50};
--warning-25: #{$warning-25};
--danger: #{$danger};
--danger-75: #{$danger-75};
--danger-50: #{$danger-50};
--danger-25: #{$danger-25};
--light: #{$light};
--dark: #{$dark};
--dark-75: #{$dark-75};
--dark-50: #{$dark-50};
--dark-25: #{$dark-25};
--background: #{$background};
--border: #{$border};
--text: #{$text};
--link: #{$link};
--link-75: #{$link-75};
--link-50: #{$link-50};
--link-25: #{$link-25};
}
*/
.has-hover-background-blue:hover {
background-color: scale-color($blue, $alpha: -90%);
@@ -192,6 +96,7 @@ $light-25: darken($high-contrast-light-gray, 45%);
// readability issues with original color
.has-text-warning {
// TODO color def
color: #ffb600 !important;
}
.has-text-warning-invert {
@@ -205,6 +110,8 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: $blue-light;
}
// TODO
/*
.has-text-high-contrast-warning {
color: $warning;
}
@@ -213,7 +120,7 @@ $light-25: darken($high-contrast-light-gray, 45%);
}
.has-text-high-contrast-light-gray {
color: $high-contrast-light-gray;
}
}*/
// border and background colors
@@ -281,7 +188,8 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: $danger-25;
}
.has-background-danger-high-contrast {
// TODO
/*.has-background-danger-high-contrast {
background-color: $high-contrast-danger;
}
.has-background-danger-high-contrast-75 {
@@ -305,7 +213,7 @@ $light-25: darken($high-contrast-light-gray, 45%);
}
.has-background-high-contrast-light-gray-25 {
background-color: $light-25;
}
}*/
// tags
.tag:not(body) {
@@ -373,10 +281,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
height: 2.5rem;
font-weight: $weight-semibold;
&[disabled] {
opacity: 1;
}
&.is-primary,
&.is-info,
&.is-link,
@@ -388,10 +292,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
}
}
&.is-warning {
color: #88550D;
}
&.is-primary:hover,
&.is-primary.is-hovered {
background-color: scale-color($primary, $lightness: -10%);
@@ -402,10 +302,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($primary, $lightness: -20%);
}
&.is-primary[disabled] {
background-color: scale-color($primary, $lightness: 75%);
}
&.is-info:hover,
&.is-info.is-hovered {
background-color: scale-color($info, $lightness: -10%);
@@ -416,10 +312,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($info, $lightness: -20%);
}
&.is-info[disabled] {
background-color: scale-color($info, $lightness: 75%);
}
&.is-link:hover,
&.is-link.is-hovered {
background-color: scale-color($link, $lightness: -10%);
@@ -430,10 +322,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($link, $lightness: -20%);
}
&.is-link[disabled] {
background-color: scale-color($link, $lightness: 75%);
}
&.is-success:hover,
&.is-success.is-hovered {
background-color: scale-color($success, $lightness: -10%);
@@ -444,10 +332,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($success, $lightness: -20%);
}
&.is-success[disabled] {
background-color: scale-color($success, $lightness: 75%);
}
&.is-warning:hover,
&.is-warning.is-hovered {
background-color: scale-color($warning, $lightness: -10%);
@@ -458,11 +342,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($warning, $lightness: -20%);
}
&.is-warning[disabled] {
background-color: scale-color($warning, $lightness: 75%);
color: #e1d4c2;
}
&.is-danger:hover,
&.is-danger.is-hovered {
background-color: scale-color($danger, $lightness: -10%);
@@ -473,10 +352,6 @@ $light-25: darken($high-contrast-light-gray, 45%);
background-color: scale-color($danger, $lightness: -20%);
}
&.is-danger[disabled] {
background-color: scale-color($danger, $lightness: 75%);
}
&.is-reduced-mobile,
&.reduced-mobile {
@media screen and (max-width: 1087px) {
@@ -520,9 +395,6 @@ $fa-font-path: "~@fortawesome/fontawesome-free/webfonts";
// NEW STYLES
//typography
.subtitle {
color: #666;
}
.has-border-white {
border-color: $white !important;
}
@@ -889,7 +761,7 @@ form .field:not(.is-grouped) {
}
a:before {
font-family: "Font Awesome 5 Free";
font-family: "Font Awesome 5 Free"; /* NOSONAR */
font-weight: 900;
-webkit-font-smoothing: antialiased;
display: inline-block;
@@ -952,3 +824,7 @@ form .field:not(.is-grouped) {
@include loader;
}
}
.sg-sub-section + .sg-sub-section {
margin-top: 2.5rem;
}

View File

@@ -0,0 +1,334 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@import "utils/_pre.scss";
// $text: $white;
$scheme-main: #050514;
$background: $grey-dark;
$text: $white-ter;
$text-strong: $white-bis;
$red: #e63453;
// TODO check if we could replace red in commons,
// without breaking the light mode
$danger: $red;
$code: lighten($red, 25%);
$primary-invert: #050514;
$info-invert: #050514;
$link-invert: #050514;
$link-hover: $white-bis;
$success-invert: #050514;
$danger-invert: #050514;
$light-gray: #f2f2f2;
$footer-background-color: $grey-dark;
$footer-color: $white-ter;
$box-background-color: scale-color($scheme-main, $lightness: 15%);
$box-background-color: $grey-darker;
$modal-card-head-background-color: $grey-dark;
$modal-card-body-background-color: $scheme-main;
$input-placeholder-color: $white;
$popover-background-color: $grey-dark;
$table-row-hover-background-color: $grey-darker;
$tooltip-background-color: $white-bis;
$tooltip-background-opacity: 0.9 !default;
$tooltip-color: $scheme-main;
// WTF
$high-contrast-background-color: #050514;
$high-contrast-light-gray: #f2f2f2;
@import "utils/_post.scss";
:root {
--sh-base-color: #fff;
--sh-font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
--sh-block-background: #000;
--sh-inline-code-color: #ff3860;
--sh-inline-code-background: #fbe7eb;
--sh-comment-color: #9a9a9a;
--sh-punctuation-color: #999999;
--sh-property-color: #2c99c7;
--sh-selector-color: #009dff;
--sh-operator-color: #999999;
--sh-operator-bg: inherit;
--sh-variable-color: #C386CA;
--sh-function-color: #FF6181;
--sh-keyword-color: #00a984;
--sh-selected-color: #7fe3cd;
--sh-highlight-background: #f5f5f5;
--sh-highlight-accent: #99d8f3;
--diff-background-color: $scheme-main;
--diff-text-color: $white-bis;
--diff-font-family: Consolas, Courier, monospace;
--diff-selection-background-color: #b3d7ff;
--diff-gutter-insert-background-color: #05C71D;
--diff-gutter-delete-background-color: #EB7A85;
--diff-gutter-selected-background-color: #fffce0;
--diff-code-insert-background-color: #05240B;
--diff-code-delete-background-color: #230608;
--diff-code-insert-edit-background-color: #c0dc91;
--diff-code-delete-edit-background-color: #000;
--diff-code-selected-background-color: #fffce0;
--diff-omit-gutter-line-color: #cb2a1d;
}
.button {
&.is-danger:hover,
&.is-danger.is-hovered {
background-color: scale-color($danger, $lightness: -2.5%);
}
&.is-danger:active,
&.is-danger.is-active {
background-color: scale-color($danger, $lightness: -4%);
}
&.is-primary,
&.is-info,
&.is-link,
&.is-success,
&.is-warning,
&.is-danger {
&.is-outlined {
background-color: inherit;
}
}
}
.menu-label {
color: $scheme-main;
}
.menu-list {
a {
color: $white-ter;
&.is-active {
background-color: $high-contrast-background-color;
}
}
}
//footer is overwritten in _main.scss
footer.footer {
background-color: $grey-dark;
a{
color: scale-color($link, $lightness:25%);
}
}
//card
.modal-card {
border: 1px solid $white-ter;
.modal-close::before, .delete::before, .modal-close::after, .delete::after {
background-color: $white-bis;
}
}
//card tables
.card-table {
tr {
a {
color: $white-bis;
}
&:hover {
td {
background-color: $grey-darker;
&.is-darker {
background-color: $grey-darker;
}
}
a {
color: $link;
}
}
}
td {
background-color: $grey-dark;
border-bottom: 1px solid $grey;
&.is-darker {
background-color: $grey-dark;
}
}
&.is-hoverable tbody tr:not(.is-selected):hover {
background-color: $grey-darker;
}
}
// panels
.panel {
border: 1px solid $white-bis;
border-radius: 0.25rem;
.panel-heading {
border: none;
border-bottom: 1px solid $border;
border-radius: 0.25rem 0.25rem 0 0;
background-color: $grey-darker;
}
.panel-footer {
background-color: $grey-darker;
color: $white-bis;
font-size: 1.25em;
font-weight: 300;
line-height: 1.25;
padding: 0.5em 0.75em;
border: none;
border-top: 1px solid $border;
border-radius: 0 0 0.25rem 0.25rem;
}
}
//diffs
.diff-code-conflict {
background-color: #332900;
}
.diff-gutter-conflict {
background-color: #FFD11A;
}
.diff-gutter-delete,.diff-gutter-insert, .diff-gutter-conflict {
color: $scheme-main;
}
/* transport meaning with more than color */
td:nth-child(2).diff-gutter-delete:before {
content: "-";
}
td:first-child.diff-gutter-insert:before {
content: "+";
}
td:first-child.diff-gutter-conflict:before {
content: "!";
}
.diff-split td:nth-child(3).diff-gutter-insert:before {
content: "+";
}
.diff-split td:first-child.diff-gutter-delete:before {
content: "-";
}
.diff-split td:nth-child(3).diff-gutter-conflict:before {
content: "!";
}
.diff-decoration-content div {
color: $scheme-main;
}
//inline member tags
.tag:not(body) {
border: 1px solid transparent;
background-color: $white;
.is-delete::before, .is-delete::after {
background-color: $scheme-main;
}
}
.tag:not(body).is-delete::before, .tag:not(body).is-delete::after {
background-color: $scheme-main;
}
.tag:not(body).is-delete:hover::before, .tag:not(body).is-delete:hover::after {
background-color: $white-bis;
}
//dark tags have light borders to separate from background
.tag:not(body).is-dark, .tag:not(body).is-black {
border: 1px solid $white-bis;
}
//blue text in light tags
.tag.is-light .has-text-link {
color: scale-color($link, $lightness: -50%) !important;
}
//outline-tags
.tag:not(body).is-outlined {
background-color: $scheme-main;
}
//cards receive white border
.box-link-shadow {
box-shadow: 0 0 0 1px $white-bis;
}
//some modals have lighter backgrounds in head
//TODO: fix with meta-class (contains light color in light-mode, dark color in dark mode
.has-background-light {
background-color: $grey-dark !important;
}
//they also often have black text, this is a stop-gap
//TODO: fix with meta-class (contains dark color in light-mode, light color in dark mode
.modal-card-title.has-text-black {
color: $white-bis !important;
}
//fix triangle for pop-overs
.popover {
color: #00a984;
}
.popover .popover-content::before
{
border-bottom-color: $grey-dark !important;
border-left-color: $grey-dark !important;
}
//make horizontal lines pop more
hr {
background-color: $white-bis;
}
//Login and logout overwrite white-ter background
.hero-body .has-background-white-ter {
background-color: $grey-dark !important;
}
//Display darker version of background image
.has-scm-background {
background-image: url(images/scmManagerHeroDark.jpg) !important;
background-size: cover;
background-position: top center;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -0,0 +1,52 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@import "utils/_pre.scss";
// colors defined in variables/commons.scss
$subtitle-color: #666;
$warning-invert: #88550D;
$button-disabled-opacity: .25;
@import "utils/_post.scss";
:root {
--sh-base-color: #363636;
--sh-font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
--sh-block-background: #ffffff;
--sh-inline-code-color: #ff3860;
--sh-inline-code-background: #fbe7eb;
--sh-comment-color: #9a9a9a;
--sh-punctuation-color: #9a9a9a;
--sh-property-color: #2c99c7;
--sh-selector-color: #005f9a;
--sh-operator-color: #686868;
--sh-operator-bg: inherit;
--sh-variable-color: #a74eb2;
--sh-function-color: #ff3860;
--sh-keyword-color: #00a984;
--sh-selected-color: #7fe3cd;
--sh-highlight-background: #f5f5f5;
--sh-highlight-accent: #99d8f3;
}

View File

@@ -0,0 +1,28 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
@import "bulma/bulma";
@import "../variables/_derived.scss";
@import "bulma-tooltip/dist/css/bulma-tooltip.min";
@import "bulma-popover/css/bulma-popover";
@import "../components/_main.scss";

View File

@@ -21,4 +21,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import "storybook-addon-i18next/register";
@import "bulma/sass/utilities/initial-variables";
@import "bulma/sass/utilities/functions";
@import "../variables/_commons.scss";

View File

@@ -0,0 +1,34 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
//$link: #007EB0;
$info: #33b2e8;
$turquoise: #00d1df;
$blue: #33b2e8;
$cyan: $blue;
$green: #00c79b;
$warning: #ffdd57;
$blue-light: #98d8f3;
$danger: #ff3860;
$family-monospace: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro", monospace;

View File

@@ -0,0 +1,55 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
$dark-75: scale-color($dark, $lightness: 25%);
$dark-50: scale-color($dark, $lightness: 50%);
$dark-25: scale-color($dark, $lightness: 75%);
$info-75: scale-color($info, $lightness: 25%);
$info-50: scale-color($info, $lightness: 50%);
$info-25: scale-color($info, $lightness: 75%);
$link-75: scale-color($link, $lightness: 25%);
$link-50: scale-color($link, $lightness: 50%);
$link-25: scale-color($link, $lightness: 75%);
$primary-75: scale-color($primary, $lightness: 25%);
$primary-50: scale-color($primary, $lightness: 50%);
$primary-25: scale-color($primary, $lightness: 75%);
$success-75: scale-color($success, $lightness: 25%);
$success-50: scale-color($success, $lightness: 50%);
$success-25: scale-color($success, $lightness: 75%);
$warning-75: scale-color($warning, $lightness: 25%);
$warning-50: scale-color($warning, $lightness: 50%);
$warning-25: scale-color($warning, $lightness: 75%);
$danger-75: scale-color($danger, $lightness: 25%);
$danger-50: scale-color($danger, $lightness: 50%);
$danger-25: scale-color($danger, $lightness: 75%);
// TODO
// $high-contrast-danger-75: scale-color($high-contrast-danger, $lightness: 25%);
// $high-contrast-danger-50: scale-color($high-contrast-danger, $lightness: 50%);
// $high-contrast-danger-25: scale-color($high-contrast-danger, $lightness: 75%);
// TODO
// high-contrast light gray
// $light-75: darken($high-contrast-light-gray, 15%);
// $light-50: darken($high-contrast-light-gray, 30%);
// $light-25: darken($high-contrast-light-gray, 45%);

View File

@@ -22,9 +22,36 @@
* SOFTWARE.
*/
const path = require("path");
const fs = require("fs");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const themes = fs
.readdirSync("src")
.map((filename) => path.parse(filename))
.filter((p) => p.ext === ".scss")
.reduce((entries, current) => ({ ...entries, [current.name]: `./src/${current.base}` }), {});
const plugins = Object.keys(themes).map(
(theme) =>
new HtmlWebpackPlugin({
filename: `${theme}.html`,
template: "./public/_theme.html",
inject: false,
theme,
})
);
plugins.push(
new HtmlWebpackPlugin({
filename: "index.html",
template: "./public/_index.html",
inject: false,
themes: Object.keys(themes),
})
);
module.exports = {
entry: "./src/scm.scss",
entry: themes,
devtool: "cheap-module-eval-source-map",
target: "web",
module: {
@@ -37,23 +64,25 @@ module.exports = {
// Translates CSS into CommonJS
"css-loader",
// Compiles Sass to CSS
"sass-loader"
]
"sass-loader",
],
},
{
test: /\.(png|svg|jpg|gif|woff2?|eot|ttf)$/,
use: ["file-loader"]
}
]
use: ["file-loader"],
},
],
},
output: {
filename: "ui-styles.bundle.js"
filename: "theme-[name].bundle.js",
},
plugins,
devServer: {
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "..", "ui-webapp", "public")],
contentBasePublicPath: ["/", "/ui-webapp"],
compress: false,
overlay: true,
port: 5000
}
port: 5000,
hot: true,
},
};

View File

@@ -14,8 +14,6 @@
<base href="{{ contextPath }}">
<title>SCM-Manager</title>
<link rel="stylesheet" type="text/css" href="{{ contextPath }}/assets/ui-styles.css">
<script>
var modernBrowser = (
'fetch' in window &&
@@ -27,6 +25,16 @@
scriptElement.src = "{{ contextPath }}/assets/polyfills.bundle.js";
document.head.appendChild(scriptElement);
}
var linkElement = document.createElement("link");
linkElement.rel = 'stylesheet';
linkElement.type = 'text/css';
var theme = localStorage.getItem('scm.theme');
if (theme === 'highcontrast') {
linkElement.href = "{{ contextPath }}/assets/ui-theme-highcontrast.css"
} else {
linkElement.href = "{{ contextPath }}/assets/ui-theme-light.css"
}
document.head.appendChild(linkElement);
</script>
</head>
<body>
@@ -36,16 +44,6 @@
<div id="root"></div>
<div id="modalRoot"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script>
window.ctxPath = "{{ contextPath }}";
window.scmStage = "{{ scmStage }}";

View File

@@ -94,7 +94,23 @@
"error-title": "Fehler",
"error-subtitle": "Das Profil kann nicht angezeigt werden.",
"error": "Fehler",
"error-message": "'me' ist nicht definiert"
"error-message": "'me' ist nicht definiert",
"theme": {
"nav": {
"label": "Design",
"title": "Wähle dein Design"
},
"subtitle": "Wähle dein Design",
"submit": "Anwenden",
"light": {
"displayName": "Hell",
"description": "'Hell' is das Standard-Design des SCM-Managers"
},
"highcontrast": {
"displayName": "Hoher Kontrast",
"description": "'Hoher Kontrast' ist ein dunkles Design mit einem hohen Kontrast"
}
}
},
"password": {
"subtitle": "Passwort ändern",

View File

@@ -95,7 +95,23 @@
"error-title": "Error",
"error-subtitle": "Cannot display profile",
"error": "Error",
"error-message": "'me' is undefined"
"error-message": "'me' is undefined",
"theme": {
"nav": {
"label": "Theme",
"title": "Choose your Theme"
},
"subtitle": "Choose your Theme",
"submit": "Activate",
"light": {
"displayName": "Light",
"description": "The light mode is the SCM-Manager default theme"
},
"highcontrast": {
"displayName": "High contrast",
"description": "The high contrast mode is a dark theme with a high contrast between the colors"
}
}
},
"password": {
"subtitle": "Change Password",

View File

@@ -22,7 +22,7 @@
* SOFTWARE.
*/
import React, { FC } from "react";
import { Redirect, Route, Switch, useRouteMatch } from "react-router-dom";
import { Redirect, Route, useRouteMatch } from "react-router-dom";
import { useTranslation } from "react-i18next";
import {
CustomQueryFlexWrappedColumns,
@@ -38,12 +38,13 @@ import {
} from "@scm-manager/ui-components";
import ChangeUserPassword from "./ChangeUserPassword";
import ProfileInfo from "./ProfileInfo";
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
import { ExtensionPoint } from "@scm-manager/ui-extensions";
import SetPublicKeys from "../users/components/publicKeys/SetPublicKeys";
import SetPublicKeysNavLink from "../users/components/navLinks/SetPublicKeysNavLink";
import SetApiKeys from "../users/components/apiKeys/SetApiKeys";
import SetApiKeysNavLink from "../users/components/navLinks/SetApiKeysNavLink";
import { useRequiredMe } from "@scm-manager/ui-api";
import Theme from "./Theme";
const Profile: FC = () => {
const match = useRouteMatch();
@@ -55,13 +56,6 @@ const Profile: FC = () => {
const canManagePublicKeys = !!me._links.publicKeys;
const canManageApiKeys = !!me._links.apiKeys;
const shouldRenderNavigation = !!(
me._links.password ||
me._links.publicKeys ||
me._links.apiKeys ||
binder.hasExtension("profile.route")
);
if (!me) {
return (
<ErrorPage
@@ -85,14 +79,13 @@ const Profile: FC = () => {
<Page title={me.displayName}>
<CustomQueryFlexWrappedColumns>
<PrimaryContentColumn>
<Route path={url} exact render={() => <ProfileInfo me={me} />} />
{shouldRenderNavigation && (
<Switch>
{mayChangePassword && <Redirect exact from={`${url}/settings/`} to={`${url}/settings/password`} />}
{canManagePublicKeys && <Redirect exact from={`${url}/settings/`} to={`${url}/settings/publicKeys`} />}
{canManageApiKeys && <Redirect exact from={`${url}/settings/`} to={`${url}/settings/apiKeys`} />}
</Switch>
)}
<Route path={url} exact>
<ProfileInfo me={me} />
</Route>
<Redirect exact from={`${url}/settings/`} to={`${url}/settings/theme`} />
<Route path={`${url}/settings/theme`} exact>
<Theme />
</Route>
{mayChangePassword && (
<Route path={`${url}/settings/password`}>
<ChangeUserPassword me={me} />
@@ -118,12 +111,17 @@ const Profile: FC = () => {
label={t("profile.informationNavLink")}
title={t("profile.informationNavLink")}
/>
{shouldRenderNavigation && (
<SubNavigation
to={`${url}/settings/`}
to={`${url}/settings/theme`}
label={t("profile.settingsNavLink")}
title={t("profile.settingsNavLink")}
>
<NavLink
to={`${url}/settings/theme`}
icon="fas fa-palette"
label={t("profile.theme.nav.label")}
title={t("profile.theme.nav.title")}
/>
{mayChangePassword && (
<NavLink to={`${url}/settings/password`} label={t("profile.changePasswordNavLink")} />
)}
@@ -131,7 +129,6 @@ const Profile: FC = () => {
<SetApiKeysNavLink user={me} apiKeyUrl={`${url}/settings/apiKeys`} />
<ExtensionPoint name="profile.setting" props={extensionProps} renderAll={true} />
</SubNavigation>
)}
</SecondaryNavigation>
</SecondaryNavigationColumn>
</CustomQueryFlexWrappedColumns>

View File

@@ -0,0 +1,101 @@
/*
* MIT License
*
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import { Radio, SubmitButton, Subtitle } from "@scm-manager/ui-components";
import { useForm } from "react-hook-form";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
const LS_KEY = "scm.theme";
const useThemeState = () => {
const [theme] = useState(localStorage.getItem(LS_KEY) || "light");
const [isLoading, setLoading] = useState(false);
const setTheme = (name: string) => {
setLoading(true);
localStorage.setItem(LS_KEY, name);
window.location.reload();
};
return { theme, setTheme, isLoading };
};
type ThemeForm = {
theme: string;
};
const themes = ["light", "highcontrast"];
const RadioColumn = styled.div`
flex: none;
width: 2rem;
`;
const Theme: FC = () => {
const { theme, setTheme, isLoading } = useThemeState();
const {
register,
setValue,
handleSubmit,
formState: { isDirty },
} = useForm<ThemeForm>({
mode: "onChange",
defaultValues: {
theme,
},
});
const [t] = useTranslation("commons");
const onSubmit = (values: ThemeForm) => {
setTheme(values.theme);
};
return (
<>
<Subtitle>{t("profile.theme.subtitle")}</Subtitle>
<form onSubmit={handleSubmit(onSubmit)}>
{themes.map((theme) => (
<div
key={theme}
onClick={() => setValue("theme", theme, { shouldDirty: true })}
className="card ml-1 mb-5 control columns is-vcentered has-cursor-pointer"
>
<RadioColumn className="column">
<Radio {...register("theme")} value={theme} disabled={isLoading} />
</RadioColumn>
<div className="column content">
<h3>{t(`profile.theme.${theme}.displayName`)}</h3>
<p>{t(`profile.theme.${theme}.description`)}</p>
</div>
</div>
))}
<SubmitButton label={t("profile.theme.submit")} loading={isLoading} disabled={!isDirty} />
</form>
</>
);
};
export default Theme;

1109
yarn.lock

File diff suppressed because it is too large Load Diff