1
Fork 0
mirror of https://github.com/Steffo99/bluelib.git synced 2024-12-22 19:44:21 +00:00

First commit (0.8.1)

This commit is contained in:
Steffo 2020-06-01 00:55:02 +02:00
commit f034bf23ba
Signed by: steffo
GPG key ID: 896A80F55F7C97F0
34 changed files with 13171 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules
/*.log
.idea/
size-plugin.json

12354
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

32
package.json Normal file
View file

@ -0,0 +1,32 @@
{
"private": false,
"name": "bluelib",
"version": "0.8.1",
"license": "AGPL-3.0-or-later",
"main": "src/index.js",
"scripts": {
"start": "preact watch",
"build": "preact build --no-prerender --dest docs && git add docs",
"ghpages": "npm run -s build && git add . && cross-env-shell git commit -m \"$npm_package_version\" && git push && cross-env-shell hub release create -m \"$npm_package_version\" \"$npm_package_version\" && cross-env-shell sentry-cli releases set-commits \"$npm_package_version\" --auto && cross-env-shell sentry-cli releases deploys \"$npm_package_version\" new --env production -n \"gh-pages\""
},
"devDependencies": {
"cross-env": "^7.0.2",
"preact-cli": "^3.0.0-rc.14"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.28",
"@fortawesome/free-brands-svg-icons": "^5.13.0",
"@fortawesome/free-regular-svg-icons": "^5.13.0",
"@fortawesome/free-solid-svg-icons": "^5.13.0",
"@fortawesome/react-fontawesome": "^0.1.9",
"css-loader": "^3.5.3",
"file-loader": "^5.0.2",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"preact": "^10.4.4",
"preact-render-to-string": "^5.1.8",
"react-syntax-highlighter": "^12.2.1",
"showdown": "^1.9.1",
"style-loader": "^1.2.1"
}
}

9
preact.config.js Normal file
View file

@ -0,0 +1,9 @@
const DefinePlugin = require("webpack/lib/DefinePlugin");
export default function (config, env, helpers) {
// noinspection JSUnresolvedVariable
config.resolve.alias["react"] = "preact/compat";
// noinspection JSUnresolvedVariable
config.resolve.alias["react-dom"] = "preact/compat";
};

View file

@ -0,0 +1,25 @@
import style from "./Box.less";
export const BoxColors = Object.freeze({
RED: style.red,
ORANGE: style.orange,
YELLOW: style.yellow,
LIME: style.lime,
CYAN: style.cyan,
BLUE: style.blue,
MAGENTA: style.magenta,
DEFAULT: style.default
})
export default function (props) {
let color = BoxColors.DEFAULT;
if(props.color) {
color = props.color;
}
return (
<div class={style.box + " " + color}>
{props.children}
</div>
);
}

View file

@ -0,0 +1,49 @@
@import "../../styles/constants.less";
.box {
padding: 8px;
border-radius: 4px;
margin: 4px;
height: calc(100% - 8px);
min-width: 256px;
}
.default {
background-color: fade(@fg, 5%);
color: @fg;
}
.red {
background-color: fade(@red, 5%);
color: @red;
}
.orange {
background-color: fade(@orange, 5%);
color: @orange;
}
.yellow {
background-color: fade(@yellow, 5%);
color: @yellow;
}
.lime {
background-color: fade(@lime, 5%);
color: @lime;
}
.cyan {
background-color: fade(@cyan, 5%);
color: @cyan;
}
.blue {
background-color: fade(@blue, 5%);
color: @blue;
}
.magenta {
background-color: fade(@magenta, 5%);
color: @magenta;
}

View file

@ -0,0 +1,5 @@
export default function(props) {
return (
<a href={props.src} title={props.alt} target={"_blank"}><img src={props.src} alt={props.alt}/></a>
)
}

View file

@ -0,0 +1,15 @@
import style from "./Panel.less";
import Box from "./Box";
export default function(props) {
return (
<Box color={props.color}>
<h3 class={style.title}>
{props.title}
</h3>
<div class={style.contents}>
{props.children}
</div>
</Box>
);
}

View file

@ -0,0 +1,10 @@
@import "../../styles/constants.less";
.title {
font-family: @title;
}
.contents {
font-family: @text;
}

View file

@ -0,0 +1,15 @@
import Split from "../Layout/Split";
import {Fragment} from "preact";
export default function (props) {
return (
<Fragment>
<h2>
{props.title}
</h2>
<Split>
{props.children}
</Split>
</Fragment>
);
}

View file

@ -0,0 +1,9 @@
import style from "./TablePanel.less";
export default function (props) {
return (
<table class={style.tablepanel}>
{props.children}
</table>
);
}

View file

@ -0,0 +1,6 @@
@import "../../styles/constants.less";
.tablepanel {
margin: 4px;
width: calc(100% - 8px);
}

View file

@ -0,0 +1,97 @@
import {Component} from 'preact'
import style from "./Timer.less"
export default class Timer extends Component {
constructor() {
super();
this.state = {
"now": Date.now()
};
this.timer = null;
}
componentDidMount() {
this.timer = setInterval(() => {
this.setState({"now": Date.now()})
}, 1000)
}
componentWillUnmount() {
if(this.timer !== null) {
clearInterval(this.timer)
}
}
render() {
let dateTo = "Unknown date";
let className = style.timer;
let parts = {
milliseconds: "?",
seconds: "?",
minutes: "?",
hours: "?",
days: "?",
};
if(this.props.to) {
dateTo = new Date(this.props.to);
let timeLeft = dateTo - this.state.now;
if(timeLeft > 0) {
parts = {
milliseconds: timeLeft % 1000,
seconds: Math.floor(timeLeft / 1000) % 60,
minutes: Math.floor(timeLeft / 60000) % 60,
hours: Math.floor(timeLeft / 3600000) % 24,
days: Math.floor(timeLeft / 86400000),
};
}
else {
parts = {
milliseconds: 0,
seconds: 0,
minutes: 0,
hours: 0,
days: 0,
};
className += " " + style.expired;
}
}
else {
className += " " + style.unknown;
}
return (
<div class={className} title={dateTo}>
<div class={style.days + " " + style.count}>
{parts.days}
</div>
<div className={style.days + " " + style.text}>
giorni
</div>
<div class={style.hours + " " + style.count}>
{parts.hours}
</div>
<div className={style.hours + " " + style.text}>
ore
</div>
<div class={style.minutes + " " + style.count}>
{parts.minutes}
</div>
<div className={style.minutes + " " + style.text}>
minuti
</div>
<div class={style.seconds + " " + style.count}>
{parts.seconds}
</div>
<div class={style.seconds + " " + style.text}>
secondi
</div>
</div>
)
}
}

View file

@ -0,0 +1,57 @@
@import "../../styles/constants";
.timer {
display: grid;
text-align: center;
align-items: center;
justify-content: center;
margin-top: 8px;
margin-bottom: 8px;
padding: 8px;
grid-template-columns: 80px 80px 80px 80px;
border: 2px solid @plusplus;
border-radius: 4px;
}
.days {
grid-column: 1;
}
.hours {
grid-column: 2;
}
.minutes {
grid-column: 3;
}
.seconds {
grid-column: 4;
}
.count {
grid-row: 1;
font-size: xx-large;
color: @accent;
}
.text {
grid-row: 2;
font-size: small;
}
.unknown {
color: @magenta;
.count {
color: @magenta;
}
}
.expired {
color: @red;
.count {
color: @red;
}
}

View file

@ -0,0 +1,10 @@
import style from "./Todo.less";
export default function (props) {
if(process.env.NODE_ENV === "development") {
return <span class={style.todo}>{props.children}</span>;
}
else {
return null;
}
}

View file

@ -0,0 +1,9 @@
@import "../../styles/constants.less";
.todo {
border: 1px yellow solid;
border-radius: 2px;
padding: 1px;
background-color: black;
color: yellow;
}

View file

@ -0,0 +1,18 @@
import style from './Footer.less';
import { Component } from 'preact';
export default function(props) {
return (
<div class={style.footer}>
© {new Date().getFullYear()}
&nbsp;-&nbsp;
<a href={"https://steffo.eu/"}>Stefano Pigozzi</a>
&nbsp;-&nbsp;
<a href="https://creativecommons.org/licenses/by-sa/4.0/">CC BY-SA 4.0</a>
&nbsp;-&nbsp;
<a href="https://github.com/Steffo99/appuntiweb">uni.steffo.eu {process.env.RELEASE}</a>
&nbsp;-&nbsp;
<a href={"https://ko-fi.com/steffo"}>Supportami</a>
</div>
);
}

View file

@ -0,0 +1,8 @@
@import "../../styles/constants.less";
.footer {
margin-top: 8px;
color: @accent;
text-align: center;
font-size: x-small;
}

View file

@ -0,0 +1,28 @@
import style from "./Split.less";
export default function (props) {
let children;
if(Array.isArray(props.children)) {
children = props.children.map(element => {
return (
<div class={style.splitchild}>
{element}
</div>
);
});
}
else {
children = (
<div class={style.splitchild}>
{props.children}
</div>
);
}
return (
<div class={style.split}>
<div class={style.splitparent}>{children}</div>
</div>
);
}

View file

@ -0,0 +1,16 @@
@import "../../styles/constants.less";
.split {
}
.splitparent {
display: flex;
flex-wrap: wrap;
}
.splitchild {
flex-grow: 1;
flex-shrink: 0;
flex-basis: 0;
}

View file

@ -0,0 +1,7 @@
import Latex from "./Latex";
export default function (props) {
return (
<Latex inline={false}>{props.children}</Latex>
);
}

View file

@ -0,0 +1,11 @@
import SyntaxHighlighter from 'react-syntax-highlighter'
import {monokai} from "react-syntax-highlighter/dist/cjs/styles/hljs";
import stripTabs from "../../utils/stripTabs";
export default function(props) {
return (
<SyntaxHighlighter language={props.language ? props.language : "plaintext"} style={monokai}>
{stripTabs(String(props.children))}
</SyntaxHighlighter>
)
}

View file

@ -0,0 +1,7 @@
import Latex from "./Latex";
export default function (props) {
return (
<Latex inline={true}>{props.children}</Latex>
);
}

View file

@ -0,0 +1,39 @@
import style from './Latex.less';
import {useContext} from "preact/hooks";
import LatexRenderColor from "../../contexts/LatexRenderColor";
import LatexDefaultInline from "../../contexts/LatexDefaultInline";
export default function(props) {
// black, blue, brown, cyan, darkgray, gray, green, lightgray, lime, magenta, olive, orange, pink, purple, red, teal, violet, white, yellow
let renderColor = useContext(LatexRenderColor);
let defaultInline = useContext(LatexDefaultInline);
let is_inline;
if(props.inline === undefined) {
is_inline = defaultInline;
}
else {
is_inline = props.inline;
}
if(is_inline) {
let equation = `\\inline {\\color{${renderColor}} ${props.children} }`;
return (
<img src={`https://latex.codecogs.com/svg.latex?${equation}`}
alt={props.children}
title={props.children}
class={style.latex + " " + style.inline}
/>
);
}
else {
let equation = `{\\color{${renderColor}} ${props.children} }`;
return (
<img src={`https://latex.codecogs.com/svg.latex?${equation}`}
alt={props.children}
title={props.children}
class={style.latex + " " + style.block}
/>
);
}
}

View file

@ -0,0 +1,14 @@
@import "../../styles/constants.less";
.latex {
}
.inline {
display: inline-block;
vertical-align: middle;
}
.block {
display: block;
}

View file

@ -0,0 +1,14 @@
import showdown from "showdown";
import stripTabs from "../../utils/stripTabs";
import style from "./Markdown.less";
export default function(props) {
let converter = new showdown.Converter({
"tables": true,
});
converter.setFlavor("github");
let html = converter.makeHtml(stripTabs(String(props.children)));
return <div class={style.markdown} dangerouslySetInnerHTML={{__html: html}}/>;
}

View file

@ -0,0 +1,12 @@
@import "../../styles/constants.less";
.markdown {
h1, h2, h3, h4, h5, h6 {
text-align: left;
}
h1 {
padding-bottom: 2px;
border-bottom: 1px solid @plusplusplus;
}
}

View file

@ -0,0 +1,9 @@
import BLatex from "./BLatex";
export default function (props) {
return (
<p>
<BLatex>{props.children}</BLatex>
</p>
);
}

View file

@ -0,0 +1,3 @@
import {createContext} from "preact";
export default createContext(true);

View file

@ -0,0 +1,3 @@
import {createContext} from "preact";
export default createContext("White");

64
src/index.js Normal file
View file

@ -0,0 +1,64 @@
import Box from "./components/Elements/Box"
import {BoxColors} from "./components/Elements/Box";
import Image from "./components/Elements/Image"
import Panel from "./components/Elements/Panel";
import Section from "./components/Elements/Section";
import TablePanel from "./components/Elements/TablePanel";
import Timer from "./components/Elements/Timer";
import Todo from "./components/Elements/Todo";
import Footer from "./components/Layout/Footer";
import Split from "./components/Layout/Split";
import Code from "./components/Rendering/Code";
import Latex from "./components/Rendering/Latex";
import BLatex from "./components/Rendering/BLatex";
import ILatex from "./components/Rendering/ILatex";
import PLatex from "./components/Rendering/PLatex";
import LatexDefaultInline from "./contexts/LatexDefaultInline";
import LatexRenderColor from "./contexts/LatexRenderColor";
import Markdown from "./components/Rendering/Markdown";
export {
Box,
BoxColors,
Image,
Panel,
Section,
TablePanel,
Timer,
Todo,
Footer,
Split,
Code,
Latex,
BLatex,
ILatex,
PLatex,
LatexDefaultInline,
LatexRenderColor,
Markdown,
};
import {Fragment} from "preact";
// noinspection JSUnusedGlobalSymbols
export default function() {
require("./styles/theme.less");
return (
<Fragment>
<h1>
bluelib
</h1>
<Split>
<Box>
Hi. I'm a box!
</Box>
<Box color={BoxColors.RED}>
Hi. I'm a box, but I'm red!
</Box>
<Box color={BoxColors.LIME}>
Hi. I was here third!
</Box>
</Split>
</Fragment>
)
}

33
src/styles/constants.less Normal file
View file

@ -0,0 +1,33 @@
@bg: #0d193b;
@bg-light: #142245;
@bg-lighter: #1c2b4f;
@bg-lightest: #233358;
@fg: #a0ccff;
@accent: #ffffff;
@link: #00caca;
@linkhover: #00ffff;
@linkactive: #a0ffff;
@plus: fade(@fg, 5%);
@plusplus: fade(@fg, 10%);
@plusplusplus: fade(@fg, 15%);
@plusplusplusplus: fade(@fg, 20%);
@red: #ff7d7d;
@orange: #ffbb7d;
@yellow: #ffff7d;
@lime: #7dff7d;
@cyan: #7dffff;
@blue: #7d7dff;
@magenta: #ff7dff;
@disabledfg: #808080;
@disabledbg: #1f1f1f;
@sans: "Arial", sans-serif;
@text: "Calibri", sans-serif;
@title: "Verdana", sans-serif;
@mono: "Consolas", "SFMono-Regular", "Liberation Mono", "Menlo", monospace;
@example: #d3a1ff;

154
src/styles/theme.less Normal file
View file

@ -0,0 +1,154 @@
@import "constants.less";
* {
box-sizing: border-box;
}
body {
background-color: @bg;
color: @fg;
font-family: @sans;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 4px;
margin-bottom: 4px;
color: @accent;
font-family: @title;
font-weight: normal;
text-align: center;
}
// By default h1 are as large as h2
h1 {
font-size: xx-large;
}
a {
color: @link;
text-decoration: none;
&:hover {
color: @linkhover;
}
&:active {
color: @linkactive;
}
}
img, iframe {
max-width: 100%;
max-height: 300px;
border-radius: 4px;
}
pre, code {
font-family: @mono;
font-size: 14px;
}
blockquote {
color: @fg;
border-left: 3px solid @plusplusplusplus;
background-color: @plus;
padding: 4px 4px 4px 8px;
margin: 8px 0;
}
input[type="text"], input[type="password"] {
color: @fg;
background-color: @bg;
border: 1px solid @plusplus;
border-radius: 4px;
padding: 4px;
font-size: medium;
&:disabled, &.disabled {
color: @disabledfg;
background-color: @disabledbg;
border-style: dotted;
cursor: not-allowed;
}
}
button {
color: @fg;
background-color: @bg;
border: 1px solid @plusplus;
border-radius: 4px;
padding: 4px;
font-size: medium;
&:hover, &.hover {
background-color: @plusplus;
border: 1px solid @fg;
color: @fg;
}
&:active, &.active {
background-color: fade(@accent, 20%);
border: 1px solid @accent;
color: @accent;
}
&:disabled, &.disabled {
color: @disabledfg;
background-color: @disabledbg;
border-style: dotted;
cursor: not-allowed;
}
}
hr {
border: 1px solid @plusplusplusplus;
margin-top: 24px;
margin-bottom: 24px;
}
table {
border-spacing: 0;
border: 2px solid @plusplus;
background-color: @plus;
border-collapse: collapse;
thead, tbody {
th, td {
padding: 4px;
border: 1px solid @plusplus;
}
}
thead {
background-color: @plusplus;
color: @accent;
}
}
li {
margin: 10px 0;
}
p:first-child {
margin-top: 0;
}
p:last-child {
margin-bottom: 0;
}
b {
color: @accent;
}
abbr {
cursor: help;
}
aside {
margin: 4px 0;
padding: 4px;
font-size: smaller;
background-color: @plus;
border-radius: 4px;
}

25
src/utils/stripTabs.js Normal file
View file

@ -0,0 +1,25 @@
export default function(input) {
let indent_regex = /^[ \t]+/;
let lines = input.split("\n").filter((line) => {
return line !== "";
});
let match = null;
for(let i = 0; i < lines.length; i++) {
match = indent_regex.exec(lines[i]);
if(match !== null) break;
}
let start;
if(match === null) {
start = 0;
}
else {
start = match[0].length;
}
return lines.map((line) => {
return line.substr(start);
}).join("\n");
}