Compare commits

...
Author SHA1 Message Date
Melina Preuß da6c2657e1 list changes 2023-06-06 22:16:12 +02:00
Lukas Obermann eb6a6f1950 refactor: migrate List component 2023-06-06 12:57:34 +02:00
Lukas Obermann 553324bc1c feat: increase root font size 2023-06-05 22:38:51 +02:00
Lukas Obermann 982827a00f refactor: rename preloadApi to ExternalAPI in renderer contexts 2023-06-05 22:25:57 +02:00
Lukas Obermann 0c0ceeb0fa refactor: scrolling page frame 2023-06-05 22:19:04 +02:00
Lukas Obermann 24d5352f04 refactor: restructure src folder to easily differenciate between processes 2023-06-05 18:13:48 +02:00
Lukas Obermann 5b9df3748b refactor!: migrate basic navigation bar 2023-06-03 22:30:19 +02:00
Lukas Obermann 5c4d058e72 refactor!: adjust titlebar for macos 2023-06-03 13:15:50 +02:00
Lukas Obermann 39486a3f29 test: switch from jest to native node test runner 2023-06-03 12:04:35 +02:00
Lukas Obermann a09a321a7a ci: use node version 20 2023-06-03 11:43:41 +02:00
Lukas Obermann dca99eae08 build(deps): remove unnecessary packages 2023-06-03 11:39:53 +02:00
Lukas Obermann fbc753b467 build(deps): prune typings where its associated packages are not included anymore 2023-06-03 11:29:07 +02:00
Lukas Obermann e86b2559d6 build(deps): remove explicit listing of packages included with @reduxjs/toolkit 2023-06-03 10:10:44 +02:00
Lukas Obermann a83b1bd9bd build(deps): remove packages conflicting with recent versions of react and redux 2023-06-03 10:09:26 +02:00
Lukas Obermann 6decdd580a build(deps): remove insecure @electron/remote package 2023-06-03 10:08:21 +02:00
Lukas Obermann 41f32f8af9 refactor: move base styles and styles for all migrated components 2023-06-03 00:15:06 +02:00
Lukas Obermann 0620d76d8d build(webpack): use webpack for bundling scss files 2023-06-03 00:09:58 +02:00
Lukas Obermann dd8a91d0f8 refactor!: adjust title bar to plain js and register window events via preload script 2023-06-02 23:59:52 +02:00
Lukas Obermann 977841899f refactor!: adjust root view to plain js 2023-06-02 23:57:47 +02:00
Lukas Obermann 81aee53e4f refactor!: put initial database into redux store 2023-06-02 23:36:22 +02:00
Lukas Obermann 992f026662 refactor!: upgrade react and redux root 2023-06-02 23:26:22 +02:00
Lukas Obermann 44d4c83030 build(ts): ignore old files 2023-06-01 10:30:11 +02:00
Lukas Obermann a88dfc4c37 build(deps): upgrade redux packages 2023-06-01 10:22:38 +02:00
Lukas Obermann 40733ed679 refactor: move assets into src folder 2023-05-31 14:45:03 +02:00
Lukas Obermann e2ca8d5df3 feat!: adjust root processes to recent security and parallelization recommendations
This commit contains multiple changes at once.
- It migrates everything to new APIs.
- It uses a separate window for interacting with updates.
- It uses preload scripts for IPC.
- It integrates the database via a separate utility process.
2023-05-31 14:44:39 +02:00
Lukas Obermann 20536d4f69 build(gen): migrate config schema building to generator 2023-05-31 14:40:22 +02:00
Lukas Obermann 0cad4919a9 build(ts): upgrade typescript config 2023-05-31 14:38:52 +02:00
Lukas Obermann f85f52a7e5 build(webpack): upgrade webpack config 2023-05-31 14:38:39 +02:00
Lukas Obermann 24933a677d build(deps): upgrade core packages 2023-05-31 14:38:01 +02:00
Lukas Obermann fcc96c0460 style(config): adjust some uncommon eslint configurations 2023-05-31 14:37:40 +02:00
Lukas Obermann a4f55d9455 build(git): ignore mac meta files and new .webpack output folder 2023-05-31 14:37:10 +02:00
Lukas Obermann f7fdc3a8a0 refactor: remove outdated reasonml bridging helpers 2023-05-31 14:35:28 +02:00
Lukas Obermann aa639a0139 build(git): change database module path 2023-05-31 14:35:15 +02:00
201 changed files with 11091 additions and 10088 deletions
+1 -4
View File
@@ -1,6 +1,3 @@
src/App/Utilities/YAML/Schema/
*.cjs
*.js
*.gen.tsx
node_modules
/app
/deploy
+9 -19
View File
@@ -8,8 +8,7 @@ plugins:
parserOptions:
ecmaVersion: latest
sourceType: module
project:
- ./tsconfig.json
project: ./tsconfig.json
ecmaFeatures:
jsx: true
env:
@@ -166,7 +165,6 @@ rules:
jsx-quotes: 2
key-spacing: 2
keyword-spacing: 2
line-comment-position: 2
lines-around-comment:
- 0
# requires an empty line before block comments
@@ -211,7 +209,6 @@ rules:
- ignoreChainWithDepth: 2
no-bitwise: 2
no-continue: 2
no-inline-comments: 2
no-lonely-if: 2
no-mixed-operators:
- 2
@@ -247,11 +244,6 @@ rules:
- blocks: never
classes: never
switches: never
padding-line-between-statements:
- 2
- blankLine: always
prev: "*"
next: return
prefer-object-spread: 2
quote-props:
- 2
@@ -291,8 +283,12 @@ rules:
prefer-const: 2
prefer-destructuring:
- 2
- object: true
array: false
- VariableDeclarator:
array: true
object: true
AssignmentExpression:
array: false
object: false
prefer-numeric-literals: 2
prefer-rest-params: 2
prefer-spread: 2
@@ -341,7 +337,6 @@ rules:
"@typescript-eslint/prefer-for-of": 2
"@typescript-eslint/prefer-includes": 2
"@typescript-eslint/prefer-readonly": 2
"@typescript-eslint/promise-function-async": 2
# "@typescript-eslint/quotes": 2
"@typescript-eslint/require-array-sort-compare": 2
"@typescript-eslint/require-await": 2
@@ -363,10 +358,7 @@ rules:
comma-spacing: 0
"@typescript-eslint/comma-spacing": 2
func-call-spacing: 0
"@typescript-eslint/func-call-spacing":
- 2
- always
- allowNewlines: true
"@typescript-eslint/func-call-spacing": 2
indent: 0
"@typescript-eslint/indent": 0
no-array-constructor: 0
@@ -399,8 +391,6 @@ rules:
- 2
- never
- beforeStatementContinuationChars: never
space-before-function-paren: 0
"@typescript-eslint/space-before-function-paren": 2
# React
react/boolean-prop-naming: 2
@@ -429,7 +419,7 @@ rules:
react/no-will-update-set-state: 2
react/prefer-es6-class: 2
react/prefer-stateless-function: 2
react/react-in-jsx-scope: 2
react/react-in-jsx-scope: 0
react/require-render-return: 2
react/self-closing-comp: 2
react/state-in-constructor:
+2 -2
View File
@@ -27,10 +27,10 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
- name: Use Node.js 20
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20
- name: Install Node.js dependencies
run: npm ci
- name: Compile JavaScript files
+2 -2
View File
@@ -27,10 +27,10 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
- name: Use Node.js 20
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20
- name: Install Node.js dependencies
run: npm ci
- name: Compile JavaScript files
+4 -4
View File
@@ -20,10 +20,10 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
- name: Use Node.js 20
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20
- name: Install Node.js dependencies
run: npm ci
- name: Compile TypeScript files
@@ -39,10 +39,10 @@ jobs:
with:
token: ${{ secrets.ACCESS_TOKEN }}
submodules: true
- name: Use Node.js 18.x
- name: Use Node.js 20
uses: actions/setup-node@v1
with:
node-version: 18.x
node-version: 20
- name: Install Node.js dependencies
run: npm ci
- name: Run linter
+3
View File
@@ -1,4 +1,7 @@
.DS_Store
node_modules/
/.webpack/**/*
!/.webpack/package.json
npm-debug.log*
.vs/
/data/
+1 -1
View File
@@ -1,3 +1,3 @@
[submodule "app/Database"]
path = app/Database
path = src/database/contents
url = https://github.com/elyukai/optolith-data.git
+3
View File
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}
Submodule app/Database updated: a43a1789d8...fe41061524
-131
View File
@@ -1,131 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "Schema/Config/Config.l10n.schema.json",
"$ref": "#/definitions/Config",
"definitions": {
"Config": {
"title": "RawConfig",
"type": "object",
"properties": {
"herolistSortOrder": {
"enum": ["name", "datemodified", "dateModified"]
},
"herolistVisibilityFilter": {
"enum": ["all", "own", "shared"]
},
"racesSortOrder": {
"enum": ["name", "cost"]
},
"racesValueVisibility": {
"type": "boolean"
},
"culturesSortOrder": {
"enum": ["name", "cost"]
},
"culturesVisibilityFilter": {
"enum": ["all", "common"]
},
"culturesValueVisibility": {
"type": "boolean"
},
"professionsSortOrder": {
"enum": ["name", "cost"]
},
"professionsVisibilityFilter": {
"enum": ["all", "common"]
},
"professionsFromExpansionsVisibility": {
"type": "boolean"
},
"professionsGroupVisibilityFilter": {
"enum": [0, 1, 2, 3]
},
"advantagesDisadvantagesCultureRatingVisibility": {
"type": "boolean"
},
"talentsSortOrder": {
"enum": ["name", "group", "ic"]
},
"talentsCultureRatingVisibility": {
"type": "boolean"
},
"combatTechniquesSortOrder": {
"enum": ["name", "group", "ic"]
},
"specialAbilitiesSortOrder": {
"enum": ["name", "group", "groupname"]
},
"spellsSortOrder": {
"enum": ["name", "group", "property", "ic"]
},
"spellsUnfamiliarVisibility": {
"type": "boolean"
},
"liturgiesSortOrder": {
"enum": ["name", "group", "ic"]
},
"equipmentSortOrder": {
"enum": ["name", "groupname", "where", "weight"]
},
"equipmentGroupVisibilityFilter": {
"type": "integer"
},
"sheetCheckAttributeValueVisibility": {
"type": "boolean"
},
"sheetUseParchment": {
"type": "boolean"
},
"sheetZoomFactor": {
"type": "number"
},
"enableActiveItemHints": {
"type": "boolean"
},
"locale": {
"type": "string"
},
"fallbackLocale": {
"type": "string"
},
"theme": {
"enum": ["light", "dark"]
},
"enableEditingHeroAfterCreationPhase": {
"type": "boolean"
},
"meleeItemTemplatesCombatTechniqueFilter": {
"type": "string"
},
"rangedItemTemplatesCombatTechniqueFilter": {
"type": "string"
},
"enableAnimations": {
"type": "boolean"
}
},
"required": [
"herolistSortOrder",
"herolistVisibilityFilter",
"racesSortOrder",
"culturesSortOrder",
"culturesVisibilityFilter",
"professionsSortOrder",
"professionsVisibilityFilter",
"professionsGroupVisibilityFilter",
"advantagesDisadvantagesCultureRatingVisibility",
"talentsSortOrder",
"talentsCultureRatingVisibility",
"combatTechniquesSortOrder",
"specialAbilitiesSortOrder",
"spellsSortOrder",
"spellsUnfamiliarVisibility",
"liturgiesSortOrder",
"equipmentSortOrder",
"equipmentGroupVisibilityFilter",
"enableActiveItemHints"
],
"additionalProperties": false
}
}
}
+16
View File
@@ -0,0 +1,16 @@
// @ts-check
import { join } from "node:path"
import { jsonSchema } from "optolith-tsjsonschemamd/renderers"
/** @type {import("optolith-tsjsonschemamd").GeneratorOptions} */
export default {
sourceDir: join("src", "shared", "schema"),
outputs: [
{
targetDir: join("src", "assets", "schema"),
renderer: jsonSchema({ spec: "Draft_07" }),
}
],
clean: true,
verbose: false
}
+7377 -7222
View File
File diff suppressed because it is too large Load Diff
+33 -42
View File
@@ -1,7 +1,7 @@
{
"name": "optolith",
"productName": "Optolith",
"version": "1.5.2-alpha.4",
"version": "2.0.0-alpha.1",
"description": "A generator for heroes for the P&P-RPG 'The Dark Eye'.",
"author": {
"name": "Lukas Obermann",
@@ -9,31 +9,28 @@
},
"homepage": "https://optolith.app",
"license": "MPL-2.0",
"main": "app/main.js",
"main": ".webpack/main.js",
"dependencies": {
"@electron/remote": "^2.0.8",
"@reduxjs/toolkit": "^1.9.5",
"adm-zip": "^0.5.1",
"ajv": "6.12.6",
"electron-devtools-installer": "^3.2.0",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"electron-extension-installer": "^1.2.0",
"electron-log": "^4.4.8",
"electron-updater": "^5.0.5",
"electron-window-state": "5.0.3",
"moment": "^2.29.4",
"mousetrap": "1.6.5",
"react": "16.13.1",
"react-custom-scrollbars": "4.2.1",
"react-dom": "16.13.1",
"optolith-database-schema": "^0.12.0",
"react": "^18.2.0",
"react-custom-scrollbars-2": "^4.5.0",
"react-dom": "^18.2.0",
"react-markdown": "^8.0.0",
"react-portal": "4.2.1",
"react-redux": "7.2.1",
"react-textfit": "1.1.0",
"redux": "4.0.5",
"react-redux": "^8.0.7",
"redux-devtools-extension": "2.13.8",
"redux-thunk": "2.3.0",
"rehype-raw": "^5.1.0",
"remark-breaks": "^2.0.2",
"remark-gfm": "^3.0.1",
"reselect": "4.0.0",
"semver": "7.3.2",
"tslib": "^2.3.1",
"yaml": "1.10.0"
@@ -41,40 +38,37 @@
"devDependencies": {
"@commitlint/cli": "11.0.0",
"@commitlint/config-conventional": "11.0.0",
"@types/classnames": "2.2.11",
"@types/jest": "26.0.15",
"@types/mousetrap": "1.6.4",
"@types/node": "14.14.7",
"@types/react": "16.9.56",
"@types/react-custom-scrollbars": "4.0.7",
"@types/react-dom": "16.9.9",
"@types/react-portal": "4.0.2",
"@types/node": "^20.2.5",
"@types/react": "^18.2.7",
"@types/react-dom": "^18.2.4",
"@types/react-redux": "7.1.11",
"@types/semver": "7.3.4",
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"custom-env": "2.0.1",
"dotenv": "8.2.0",
"electron": "^19.0.8",
"css-loader": "^6.8.1",
"electron": "^24.1.3",
"electron-builder": "^23.1.0",
"electron-notarize": "^1.2.1",
"eslint": "^8.7.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-tsdoc": "^0.2.14",
"fs-extra": "9.0.1",
"gentype": "3.38.0",
"html-webpack-plugin": "^5.5.0",
"husky": "^4.3.8",
"jest": "^26.6.3",
"sass": "1.29.0",
"glob": "^10.2.6",
"html-webpack-plugin": "^5.5.1",
"mini-css-extract-plugin": "^2.7.6",
"optolith-tsjsonschemamd": "^0.9.3",
"resolve-url-loader": "^5.0.0",
"sass": "^1.62.1",
"sass-loader": "^13.3.1",
"ssh2-sftp-client": "^9.0.1",
"standard-version": "^9.3.1",
"ts-jest": "^26.5.6",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "^5.67.0",
"webpack-cli": "^4.9.2"
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.2",
"tsx": "^3.12.7",
"typescript": "^5.0.4",
"webpack": "^5.84.1",
"webpack-cli": "^5.1.1"
},
"scripts": {
"prerelease:win": "set NODE_ENV=development && node ./deploy/buildInsiderWin.js",
@@ -90,17 +84,14 @@
"js:watch": "webpack -w",
"ts:build": "tsc",
"ts:watch": "tsc -w",
"createschemas": "otjsmd",
"lint": "eslint \"src/\" --ext \".ts\" --ext \".tsx\"",
"start": "electron .",
"test": "jest",
"test": "glob -c \"node --loader tsx --test\" \"./src/shared/**/*.test.ts\"",
"circ": "madge -c ./src",
"release:main": "standard-version --skip.commit --skip.tag",
"release:pre": "standard-version --prerelease=alpha"
},
"repository": "github:elyukai/optolith-client",
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
"type": "module"
}
-11
View File
@@ -1,11 +0,0 @@
#about {
.scroll-inner {
padding-top: 20px;
}
.third-party-software-body {
font-size: 12px;
line-height: normal;
margin-top: 20px;
}
}
-47
View File
@@ -1,47 +0,0 @@
import * as fs from "fs"
import * as path from "path"
import * as React from "react"
import { L10n } from "../../Models/Wiki/L10n"
import { StaticData, StaticDataRecord } from "../../Models/Wiki/WikiModel"
import { app_path } from "../../Selectors/envSelectors"
import { Markdown } from "../Universal/Markdown"
import { Page } from "../Universal/Page"
import { Scroll } from "../Universal/Scroll"
const getPath =
(staticData: StaticDataRecord) =>
path.join (
app_path,
"app",
"Docs",
L10n.A.id (StaticData.A.ui (staticData)),
"CHANGELOG.md"
)
interface LastChangesProps {
staticData: StaticDataRecord
}
export const LastChanges: React.FC<LastChangesProps> = ({ staticData }) => {
const [ text, setText ] = React.useState<string> ("...")
React.useEffect (
() => {
fs.promises.readFile (getPath (staticData), "utf-8")
.then (setText)
.catch (err => {
console.error (err)
setText ("Changelog could not be loaded")
})
},
[ staticData ]
)
return (
<Page id="last-changes">
<Scroll className="text">
<Markdown source={text} />
</Scroll>
</Page>
)
}
@@ -1,37 +0,0 @@
import * as fs from "fs"
import * as path from "path"
import * as React from "react"
import { app_path, current_version } from "../../Selectors/envSelectors"
import { Page } from "../Universal/Page"
import { Scroll } from "../Universal/Scroll"
export const ThirdPartyLicenses = () => {
const [ text, setText ] = React.useState<string> ("...")
React.useEffect (
() => {
fs.promises.readFile (path.join (app_path, "LICENSE"), "utf-8")
.then (setText)
.catch (err => {
console.error (err)
setText ("Third-party licenses could not be loaded")
})
},
[]
)
return (
<Page id="last-changes">
<Scroll className="text">
<h2>
{"Optolith Desktop Client v"}
{current_version}
</h2>
<p>{"Third Party Software and Content Licenses"}</p>
<pre className="third-party-software-body">
{text}
</pre>
</Scroll>
</Page>
)
}
-128
View File
@@ -1,128 +0,0 @@
html, body, #bodywrapper, #body {
width: 100%;
height: 100%;
position: relative;
margin: 0;
overflow: hidden;
padding: 0;
}
#body {
cursor: default;
background: var(--background-color);
display: flex;
font: 14px Alegreya;
border: 1px solid var(--separator-color-transparent);
}
.background-image {
position: absolute;
width: 120vmax;
height: 120vmax;
display: flex;
justify-content: center;
align-items: center;
align-self: center;
left: -10vmax;
> img {
opacity: .15;
width: 100vmax;
height: 100vmax;
}
}
#body.show-animations {
@keyframes rotateBackground {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.background-image > img {
animation-name: rotateBackground;
animation-timing-function: linear;
animation-duration: 300s;
animation-iteration-count: infinite;
}
}
.loading-wrapper {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
@keyframes rotateLoading {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading {
width: 80px;
height: 80px;
border-radius: 50%;
border-width: 3px;
border-color: var(--accent-color) transparent;
border-style: solid;
animation-name: rotateLoading;
animation-timing-function: linear;
animation-duration: 2s;
animation-iteration-count: infinite;
}
.loading-text {
margin-top: 24px;
color: var(--accent-color);
font: 500 14px "Alegreya Sans";
}
}
section#content {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
position: relative;
}
.small-info-text {
font: 500 12px Alegreya Sans;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--separator-color-halftransparent);
}
ul:not(.list-wrapper) {
list-style: none outside none;
margin: 0;
padding: 0;
li {
padding-left: 18px;
&::before {
position: absolute;
content: "";
transform: rotate(45deg);
width: 6px;
height: 6px;
background: var(--separator-color-transparent);
margin-top: 8px;
margin-left: -17px;
}
em {
font-weight: 400;
}
}
}
-207
View File
@@ -1,207 +0,0 @@
import * as React from "react"
import { List, splitOn } from "../../Data/List"
import { fromMaybe, guardReplace, Just, listToMaybe, Maybe } from "../../Data/Maybe"
import { AlertsContainer } from "../Containers/AlertsContainer"
import { DownloaderContainer } from "../Containers/DownloaderContainer"
import { NavigationBarContainer } from "../Containers/NavigationBarContainer"
import { Theme } from "../Models/Config"
import { HeroModelRecord } from "../Models/Hero/HeroModel"
import { L10n } from "../Models/Wiki/L10n"
import { StaticData, StaticDataRecord } from "../Models/Wiki/WikiModel"
import { classListMaybe } from "../Utilities/CSS"
import { getSystemLocale } from "../Utilities/IOUtils"
import { TabId } from "../Utilities/LocationUtils"
import { pipe_ } from "../Utilities/pipe"
import { Router } from "./Router/Router"
import { Scroll } from "./Universal/Scroll"
import { TitleBar } from "./Universal/TitleBar"
export interface AppOwnProps {}
export interface AppStateProps {
currentTab: TabId
staticData: StaticDataRecord
mhero: Maybe<HeroModelRecord>
platform: string
theme: Theme
areAnimationsEnabled: boolean
isLoading: boolean
hasInitWithError: boolean
}
export interface AppDispatchProps {
minimize (): void
maximize (): void
restore (): void
close (): void
closeDuringLoad (): void
enterFullscreen (): void
leaveFullscreen (): void
checkForUpdates (): void
}
export type AppProps = AppStateProps & AppDispatchProps & AppOwnProps
export interface AppState {
hasError?: {
error: Error
info: any
}
}
export class App extends React.Component<AppProps, AppState> {
state: AppState = {}
componentDidCatch (error: any, info: any) {
this.setState (() => ({ hasError: { error, info } }))
}
render () {
const {
currentTab,
staticData,
mhero,
platform,
theme,
areAnimationsEnabled,
minimize,
maximize,
restore,
close,
closeDuringLoad,
enterFullscreen,
leaveFullscreen,
checkForUpdates,
isLoading,
hasInitWithError,
} = this.props
const { hasError } = this.state
if (typeof hasError === "object") {
return (
<div id="body" className={`theme-${theme}`}>
<Scroll className="error-message">
<h4>{"Error"}</h4>
<p>{hasError.error.stack}</p>
<h4>{"Component Stack"}</h4>
<p>{hasError.info.componentStack}</p>
</Scroll>
</div>
)
}
if (isLoading) {
return (
<div
id="body"
className={classListMaybe (List (
Just (`theme-${theme}`),
Just (`platform-${platform}`)
))}
lang={fromMaybe ("") (listToMaybe (splitOn ("-") (getSystemLocale ())))}
>
<div className="background-image">
<img src="images/background.svg" alt="" />
</div>
<div className="loading-wrapper">
<div className="loading" />
<div className="loading-text">
{getSystemLocale () === "de-DE"
? "Lade und überprüfe Tabellen und Nutzerdaten..."
: "Loading and validating tables and user data..."}
</div>
</div>
<AlertsContainer />
<TitleBar
close={closeDuringLoad}
enterFullscreen={enterFullscreen}
leaveFullscreen={leaveFullscreen}
maximize={maximize}
minimize={minimize}
platform={platform}
restore={restore}
/>
</div>
)
}
if (hasInitWithError) {
return (
<div
id="body"
className={classListMaybe (List (
Just (`theme-${theme}`),
Just (`platform-${platform}`)
))}
lang={fromMaybe ("") (listToMaybe (splitOn ("-") (getSystemLocale ())))}
>
<div className="background-image">
<img src="images/background.svg" alt="" />
</div>
<AlertsContainer />
<TitleBar
close={closeDuringLoad}
enterFullscreen={enterFullscreen}
leaveFullscreen={leaveFullscreen}
maximize={maximize}
minimize={minimize}
platform={platform}
restore={restore}
/>
</div>
)
}
return (
<div
id="body"
className={classListMaybe (List (
Just (`theme-${theme}`),
Just (`platform-${platform}`),
guardReplace (areAnimationsEnabled) ("show-animations")
))}
lang={pipe_ (
staticData,
StaticData.A.ui,
L10n.A.id,
splitOn ("-"),
listToMaybe,
fromMaybe ("")
)}
>
<div className="background-image">
<img src="images/background.svg" alt="" />
</div>
<AlertsContainer />
<DownloaderContainer staticData={staticData} />
<TitleBar
close={close}
enterFullscreen={enterFullscreen}
leaveFullscreen={leaveFullscreen}
maximize={maximize}
minimize={minimize}
platform={platform}
restore={restore}
/>
<section id="content">
<NavigationBarContainer
staticData={staticData}
checkForUpdates={checkForUpdates}
mhero={mhero}
/>
<Router
key={currentTab}
id={currentTab}
staticData={staticData}
mhero={mhero}
/>
</section>
</div>
)
}
}
-5
View File
@@ -1,5 +0,0 @@
@import "NavigationBarBack";
@import "NavigationBarLeft";
@import "NavigationBarRight";
@import "NavigationBarTabs";
@import "NavigationBarWrapper";
-197
View File
@@ -1,197 +0,0 @@
import * as remote from "@electron/remote"
import * as React from "react"
import { fmap } from "../../../Data/Functor"
import { List } from "../../../Data/List"
import { fromMaybe, Maybe, maybe } from "../../../Data/Maybe"
import { Record } from "../../../Data/Record"
import { SettingsContainer } from "../../Containers/SettingsContainer"
import { HeroModelRecord } from "../../Models/Hero/HeroModel"
import { SubTab } from "../../Models/Hero/heroTypeHelpers"
import { AdventurePointsCategories } from "../../Models/View/AdventurePointsCategories"
import { NavigationBarTabOptions } from "../../Models/View/NavigationBarTabOptions"
import { StaticDataRecord } from "../../Models/Wiki/WikiModel"
import { translate, translateP } from "../../Utilities/I18n"
import { TabId } from "../../Utilities/LocationUtils"
import { signNeg } from "../../Utilities/NumberUtils"
import { pipe, pipe_ } from "../../Utilities/pipe"
import { AvatarWrapper } from "../Universal/AvatarWrapper"
import { BorderButton } from "../Universal/BorderButton"
import { IconButton } from "../Universal/IconButton"
import { Text } from "../Universal/Text"
import { TooltipToggle } from "../Universal/TooltipToggle"
import { ApTooltip } from "./ApTooltip"
import { NavigationBarBack } from "./NavigationBarBack"
import { NavigationBarLeft } from "./NavigationBarLeft"
import { NavigationBarRight } from "./NavigationBarRight"
import { NavigationBarSubTabs } from "./NavigationBarSubTabs"
import { NavigationBarTabs } from "./NavigationBarTabs"
import { NavigationBarWrapper } from "./NavigationBarWrapper"
const toggleDevtools = () => {
remote.getCurrentWindow ().webContents.toggleDevTools ()
}
export interface NavigationBarOwnProps {
mhero: Maybe<HeroModelRecord>
staticData: StaticDataRecord
checkForUpdates (): void
}
export interface NavigationBarStateProps {
currentTab: TabId
avatar: Maybe<string>
isRedoAvailable: boolean
isRemovingEnabled: boolean
isUndoAvailable: boolean
isSettingsOpen: boolean
isHeroSection: boolean
tabs: List<Record<NavigationBarTabOptions>>
subtabs: Maybe<List<SubTab>>
adventurePoints: Maybe<Record<AdventurePointsCategories>>
maximumForMagicalAdvantagesDisadvantages: Maybe<number>
isSpellcaster: boolean
isBlessedOne: boolean
}
export interface NavigationBarDispatchProps {
undo (): void
redo (): void
saveHero (): void
saveGroup (): void
setTab (id: TabId): void
openSettings (): void
closeSettings (): void
}
export type NavigationBarProps =
NavigationBarStateProps & NavigationBarDispatchProps & NavigationBarOwnProps
export const NavigationBar: React.FC<NavigationBarProps> = props => {
const {
currentTab,
tabs,
subtabs: msubtabs,
openSettings,
closeSettings,
isHeroSection,
avatar,
staticData,
undo,
isRedoAvailable,
isUndoAvailable,
redo,
saveHero,
setTab,
adventurePoints: m_ap,
maximumForMagicalAdvantagesDisadvantages,
isSpellcaster,
isBlessedOne,
isSettingsOpen,
checkForUpdates,
} = props
const handleHerolistTab = React.useCallback (() => setTab (TabId.Herolist), [ setTab ])
return (
<>
<NavigationBarWrapper>
<NavigationBarLeft>
{isHeroSection
? (
<>
<NavigationBarBack handleSetTab={handleHerolistTab} />
<AvatarWrapper src={avatar} />
</>
)
: null}
<NavigationBarTabs
currentTab={currentTab}
tabs={tabs}
/>
</NavigationBarLeft>
<NavigationBarRight>
{isHeroSection
? (
<>
{maybe (<Text className="collected-ap">
{translateP (staticData) ("header.apleft") (List ("X"))}
</Text>)
((ap: Record<AdventurePointsCategories>) => (
<TooltipToggle
position="bottom"
margin={12}
content={
<ApTooltip
adventurePoints={ap}
staticData={staticData}
maximumForMagicalAdvantagesDisadvantages={
maximumForMagicalAdvantagesDisadvantages
}
isSpellcaster={isSpellcaster}
isBlessedOne={isBlessedOne}
/>
}
target={
<Text className="collected-ap">
{translateP (staticData)
("header.apleft")
(List (
pipe_ (
m_ap,
fmap (pipe (
AdventurePointsCategories.A.available,
signNeg
)),
fromMaybe <string | number > ("")
)
))}
</Text>
}
/>
))
(m_ap)}
<IconButton
icon="&#xE90f;"
onClick={undo}
disabled={!isUndoAvailable}
/>
<IconButton
icon="&#xE910;"
onClick={redo}
disabled={!isRedoAvailable}
/>
<BorderButton
label={translate (staticData) ("header.savebtn")}
onClick={saveHero}
/>
</>
)
: null}
<IconButton
icon="&#xE906;"
onClick={openSettings}
/>
<SettingsContainer
staticData={staticData}
isSettingsOpen={isSettingsOpen}
close={closeSettings}
checkForUpdates={checkForUpdates}
/>
<IconButton
icon="&#xE911;"
onClick={toggleDevtools}
/>
</NavigationBarRight>
</NavigationBarWrapper>
{maybe (null as React.ReactNode)
((subtabs: List<SubTab>) => (
<NavigationBarSubTabs
tabs={subtabs}
currentTab={currentTab}
setTab={setTab}
/>
))
(msubtabs)}
</>
)
}
@@ -1,75 +0,0 @@
import * as remote from "@electron/remote"
import * as React from "react"
import { useDispatch } from "react-redux"
import { setTab } from "../../Actions/LocationActions"
import { SettingsContainer } from "../../Containers/SettingsContainer"
import { StaticDataRecord } from "../../Models/Wiki/WikiModel"
import { translate } from "../../Utilities/I18n"
import { TabId } from "../../Utilities/LocationUtils"
import { BorderButton } from "../Universal/BorderButton"
import { IconButton } from "../Universal/IconButton"
import { Text } from "../Universal/Text"
import { NavigationBarBack } from "./NavigationBarBack"
import { NavigationBarLeft } from "./NavigationBarLeft"
import { NavigationBarRight } from "./NavigationBarRight"
import { NavigationBarWrapper } from "./NavigationBarWrapper"
interface Props {
staticData: StaticDataRecord
groupName: string
isSettingsOpen: boolean
closeSettings (): void
openSettings (): void
saveGroup (): void
checkForUpdates (): void
}
const toggleDevtools = remote.getCurrentWindow ().webContents.toggleDevTools
export const NavigationBarForGroup: React.FC<Props> = props => {
const {
staticData,
groupName,
isSettingsOpen,
closeSettings,
openSettings,
saveGroup,
checkForUpdates,
} = props
const dispatch = useDispatch ()
const handleSetTab = React.useCallback (
() => dispatch (setTab (TabId.Grouplist)),
[ dispatch ]
)
return (
<NavigationBarWrapper>
<NavigationBarLeft>
<NavigationBarBack handleSetTab={handleSetTab} />
<Text>{groupName}</Text>
</NavigationBarLeft>
<NavigationBarRight>
<BorderButton
label={translate (staticData) ("header.savebtn")}
onClick={saveGroup}
/>
<IconButton
icon="&#xE906;"
onClick={openSettings}
/>
<SettingsContainer
staticData={staticData}
isSettingsOpen={isSettingsOpen}
close={closeSettings}
checkForUpdates={checkForUpdates}
/>
<IconButton
icon="&#xE911;"
onClick={toggleDevtools}
/>
</NavigationBarRight>
</NavigationBarWrapper>
)
}
@@ -1,25 +0,0 @@
.navigationbar-left {
flex: 1 1 100%;
overflow: hidden;
display: flex;
align-items: stretch;
> div {
margin-left: 24px;
&.avatar-wrapper {
flex-shrink: 0;
align-self: center;
width: 40px;
height: 40px;
margin-left: 28px;
margin-right: 4px;
}
&.text {
color: var(--headings-color);
text-transform: uppercase;
letter-spacing: 0.1em;
}
}
}
@@ -1,9 +0,0 @@
import * as React from "react"
interface Props { }
export const NavigationBarLeft: React.FC<Props> = ({ children }) => (
<div className="navigationbar-left">
{children}
</div>
)
@@ -1,9 +0,0 @@
import * as React from "react"
interface Props { }
export const NavigationBarRight: React.FC<Props> = ({ children }) => (
<div className="navigationbar-right">
{children}
</div>
)
@@ -1,29 +0,0 @@
import * as React from "react"
import { SubTab } from "../../Models/Hero/heroTypeHelpers"
import { TabId } from "../../Utilities/LocationUtils"
import { Tab } from "../Universal/Tab"
interface Props {
currentTab: TabId
tab: SubTab
setTab (tab: TabId): void
}
export const NavigationBarSubTab: React.FC<Props> = props => {
const { currentTab, tab, setTab } = props
const { id, label, disabled } = tab
const is_active = currentTab === id
const handleTab = React.useCallback (() => setTab (id), [ setTab, id ])
return (
<Tab
active={is_active}
onClick={handleTab}
label={label}
disabled={disabled}
/>
)
}
@@ -1,73 +0,0 @@
.navigationbar-subtabs {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 32px;
flex: none;
&::after {
position: absolute;
content: "";
width: 100%;
height: 1px;
background: var(--separator-color-transparent);
bottom: 0;
z-index: 1;
}
> .tab {
font: bold 13px/32px Alegreya SC;
flex: 0 1 auto;
letter-spacing: 0.1em;
margin: 0 20px;
text-transform: uppercase;
position: relative;
color: var(--accent-color);
min-width: 0;
z-index: 2;
&:hover {
color: var(--headings-color);
cursor: pointer;
}
&:active, &:focus {
color: var(--accent-color-intense);
cursor: pointer;
}
&:last-child {
margin-right: 20px;
}
&.active {
pointer-events: none;
color: var(--headings-color);
&:after {
position: absolute;
content: "";
width: auto;
height: 2px;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
background: var(--accent-color);
}
}
&.disabled {
pointer-events: none;
color: var(--separator-color-halftransparent);
}
div {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
@@ -1,33 +0,0 @@
import * as React from "react"
import { List, map, toArray } from "../../../Data/List"
import { SubTab } from "../../Models/Hero/heroTypeHelpers"
import { TabId } from "../../Utilities/LocationUtils"
import { pipe_ } from "../../Utilities/pipe"
import { NavigationBarSubTab } from "./NavigationBarSubTab"
interface Props {
currentTab: TabId
tabs: List<SubTab>
setTab (tab: TabId): void
}
export const NavigationBarSubTabs: React.FC<Props> = props => {
const { currentTab, tabs, setTab } = props
return (
<div className="navigationbar-subtabs">
{pipe_ (
tabs,
map (tab => (
<NavigationBarSubTab
key={tab .id}
currentTab={currentTab}
tab={tab}
setTab={setTab}
/>
)),
toArray
)}
</div>
)
}
-47
View File
@@ -1,47 +0,0 @@
import * as React from "react"
import { useDispatch } from "react-redux"
import { elem, notNull } from "../../../Data/List"
import { Record } from "../../../Data/Record"
import { setTab } from "../../Actions/LocationActions"
import { NavigationBarTabOptions } from "../../Models/View/NavigationBarTabOptions"
import { TabId } from "../../Utilities/LocationUtils"
import { Tab } from "../Universal/Tab"
const NBTOA = NavigationBarTabOptions.A
interface Props {
className?: string
currentTab: TabId
options: Record<NavigationBarTabOptions>
}
export const NavigationBarTab: React.FC<Props> = props => {
const {
className,
currentTab,
options,
} = props
const id = NBTOA.id (options)
const label = NBTOA.label (options)
const subTabs = NBTOA.subTabs (options)
const dispatch = useDispatch ()
const handleSetTab = React.useCallback (
() => dispatch (setTab (id)),
[ dispatch, id ]
)
const isActive = notNull (subTabs) ? elem (currentTab) (subTabs) : currentTab === id
return (
<Tab
key={id}
active={isActive}
onClick={handleSetTab}
className={className}
label={label}
/>
)
}
@@ -1,63 +0,0 @@
.navigationbar-tabs {
flex: 1 1 100%;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
align-self: stretch;
.tab {
font: bold 15px/var(--navigationbar-height) Alegreya SC;
// font: bold 18px/#{$titlebar-height} Alegreya SC;
flex: 0 1 auto;
letter-spacing: 0.1em;
margin: 0 20px;
text-transform: uppercase;
color: var(--accent-color);
min-width: 0;
// padding: 0 10px;
position: relative;
&:first-child {
margin-left: 0;
}
&:hover {
color: var(--headings-color);
cursor: pointer;
}
&:active, &:focus {
color: var(--accent-color-intense);
cursor: pointer;
}
&.active {
pointer-events: none;
color: var(--headings-color);
&::after {
position: absolute;
content: "";
width: auto;
height: 3px;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
background: var(--accent-color);
}
}
&.disabled {
pointer-events: none;
color: var(--separator-color-halftransparent);
}
div {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
@@ -1,34 +0,0 @@
import * as React from "react"
import { List, map, toArray } from "../../../Data/List"
import { Record } from "../../../Data/Record"
import { NavigationBarTabOptions } from "../../Models/View/NavigationBarTabOptions"
import { TabId } from "../../Utilities/LocationUtils"
import { pipe_ } from "../../Utilities/pipe"
import { NavigationBarTab } from "./NavigationBarTab"
const NBTOA = NavigationBarTabOptions.A
interface Props {
currentTab: TabId
tabs: List<Record<NavigationBarTabOptions>>
}
export const NavigationBarTabs: React.FC<Props> = props => {
const { currentTab, tabs } = props
return (
<div className="navigationbar-tabs">
{pipe_ (
tabs,
map (tab => (
<NavigationBarTab
key={NBTOA.id (tab)}
currentTab={currentTab}
options={tab}
/>
)),
toArray
)}
</div>
)
}
@@ -1,11 +0,0 @@
import * as React from "react"
interface Props { }
export const NavigationBarWrapper: React.FC<Props> = ({ children }) => (
<div className="navigationbar">
<div className="navigationbar-inner">
{children}
</div>
</div>
)
-171
View File
@@ -1,171 +0,0 @@
import * as React from "react"
import { Maybe, maybe } from "../../../Data/Maybe"
import { AdvantagesContainer } from "../../Containers/AdvantagesContainer"
import { AttributesContainer } from "../../Containers/AttributesContainer"
import { CombatTechniquesContainer } from "../../Containers/CombatTechniquesContainer"
import { CulturesContainer } from "../../Containers/CulturesContainer"
import { DisadvantagesContainer } from "../../Containers/DisadvantagesContainer"
import { EquipmentContainer } from "../../Containers/EquipmentContainer"
import { HelpContainer } from "../../Containers/HelpContainer"
import { HerolistContainer } from "../../Containers/HerolistContainer"
import { HitZoneArmorsContainer } from "../../Containers/HitZoneArmorsContainer"
import { LiturgicalChantsContainer } from "../../Containers/LiturgicalChantsContainer"
import { PactContainer } from "../../Containers/PactContainer"
import { PersonalDataContainer } from "../../Containers/PersonalDataContainer"
import { PetsContainer } from "../../Containers/PetsContainer"
import { ProfessionsContainer } from "../../Containers/ProfessionsContainer"
import { RacesContainer } from "../../Containers/RacesContainer"
import { RulesContainer } from "../../Containers/RulesContainer"
import { SheetsContainer } from "../../Containers/SheetsContainer"
import { SkillsContainer } from "../../Containers/SkillsContainer"
import { SpecialAbilitiesContainer } from "../../Containers/SpecialAbilitiesContainer"
import { SpellsContainer } from "../../Containers/SpellsContainer"
import { WikiContainer } from "../../Containers/WikiContainer"
import { HeroModelRecord } from "../../Models/Hero/HeroModel"
import { StaticDataRecord } from "../../Models/Wiki/WikiModel"
import { TabId } from "../../Utilities/LocationUtils"
import { Imprint } from "../About/Imprint"
import { LastChanges } from "../About/LastChanges"
import { ThirdPartyLicenses } from "../About/ThirdPartyLicenses"
import { Grouplist } from "../Groups/Grouplist"
import { MainContent } from "../Universal/MainContent"
import { Page } from "../Universal/Page"
import { Scroll } from "../Universal/Scroll"
interface Props {
id: TabId
staticData: StaticDataRecord
mhero: Maybe<HeroModelRecord>
}
interface State {
hasError?: {
error: Error
info: any
}
}
export class Router extends React.Component<Props> {
state: State = {}
componentDidCatch (error: any, info: any) {
this.setState (() => ({ hasError: { error, info } }))
}
render (): React.ReactNode {
const { id, staticData, mhero } = this.props
const { hasError } = this.state
if (typeof hasError === "object") {
return (
<Page>
<MainContent>
<Scroll className="error-message">
<h4>{"Error"}</h4>
<p>{hasError.error.stack}</p>
<h4>{"Component Stack"}</h4>
<p>{hasError.info.componentStack}</p>
</Scroll>
</MainContent>
</Page>
)
}
const unwrapWithHero =
(f: (hero: HeroModelRecord) => JSX.Element) =>
maybe (null as React.ReactNode) (f) (mhero)
const VIEWS: { [K in TabId]: () => React.ReactNode } = {
[TabId.Herolist]: () => <HerolistContainer staticData={staticData} />,
[TabId.Grouplist]: () => <Grouplist />,
[TabId.Wiki]: () => <WikiContainer staticData={staticData} />,
[TabId.Faq]: () => <HelpContainer staticData={staticData} />,
[TabId.Imprint]: () => <Imprint staticData={staticData} />,
[TabId.ThirdPartyLicenses]: () => <ThirdPartyLicenses />,
[TabId.LastChanges]: () => <LastChanges staticData={staticData} />,
[TabId.Profile]:
() => unwrapWithHero (hero => (
<PersonalDataContainer staticData={staticData} hero={hero} />
)),
[TabId.PersonalData]:
() => unwrapWithHero (hero => (
<PersonalDataContainer staticData={staticData} hero={hero} />
)),
[TabId.CharacterSheet]:
() => unwrapWithHero (hero => (
<SheetsContainer staticData={staticData} hero={hero} />
)),
[TabId.Pact]:
() => unwrapWithHero (hero => (
<PactContainer staticData={staticData} hero={hero} />
)),
[TabId.Rules]:
() => unwrapWithHero (hero => (
<RulesContainer staticData={staticData} hero={hero} />
)),
[TabId.Races]:
() => unwrapWithHero (hero => (
<RacesContainer staticData={staticData} hero={hero} />
)),
[TabId.Cultures]:
() => unwrapWithHero (hero => (
<CulturesContainer staticData={staticData} hero={hero} />
)),
[TabId.Professions]:
() => unwrapWithHero (hero => (
<ProfessionsContainer staticData={staticData} hero={hero} />
)),
[TabId.Attributes]:
() => unwrapWithHero (hero => (
<AttributesContainer staticData={staticData} hero={hero} />
)),
[TabId.Advantages]:
() => unwrapWithHero (hero => (
<AdvantagesContainer staticData={staticData} hero={hero} />
)),
[TabId.Disadvantages]:
() => unwrapWithHero (hero => (
<DisadvantagesContainer staticData={staticData} hero={hero} />
)),
[TabId.Skills]:
() => unwrapWithHero (hero => (
<SkillsContainer staticData={staticData} hero={hero} />
)),
[TabId.CombatTechniques]:
() => unwrapWithHero (hero => (
<CombatTechniquesContainer staticData={staticData} hero={hero} />
)),
[TabId.SpecialAbilities]:
() => unwrapWithHero (hero => (
<SpecialAbilitiesContainer staticData={staticData} hero={hero} />
)),
[TabId.Spells]:
() => unwrapWithHero (hero => (
<SpellsContainer staticData={staticData} hero={hero} />
)),
[TabId.LiturgicalChants]:
() => unwrapWithHero (hero => (
<LiturgicalChantsContainer staticData={staticData} hero={hero} />
)),
[TabId.Equipment]:
() => unwrapWithHero (hero => (
<EquipmentContainer staticData={staticData} hero={hero} />
)),
[TabId.ZoneArmor]:
() => unwrapWithHero (hero => (
<HitZoneArmorsContainer staticData={staticData} hero={hero} />
)),
[TabId.Pets]: () => <PetsContainer staticData={staticData} />,
// master: <Master />
}
return VIEWS [id] ()
}
}
-43
View File
@@ -1,43 +0,0 @@
.btn {
height: auto;
min-height: 32px;
color: var(--accent-color);
text-align: center;
overflow: hidden;
border: 1px solid var(--accent-color);
&:hover, &:active {
cursor: pointer;
}
&.disabled {
border-color: var(--separator-color-transparent);
background: none;
color: var(--separator-color-halftransparent);
pointer-events: none;
&:hover {
border-color: var(--separator-color-transparent);
color: var(--separator-color-halftransparent);
}
}
&.active {
background: var(--accent-color);
color: var(--background-color);
pointer-events: none;
&:hover {
border-color: var(--accent-color);
}
}
&.autoWidth {
width: auto;
min-width: auto;
}
&.btn-flat {
border: none;
}
}
-67
View File
@@ -1,67 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { guardReplace, Just, Maybe, maybe, normalize, or, orN } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
import { TooltipHint } from "./TooltipHint"
interface Props {
active?: boolean
autoWidth?: boolean
className?: string
disabled?: boolean | Maybe<boolean>
flat?: boolean
fullWidth?: boolean
hint?: Maybe<string>
primary?: boolean
round?: boolean
onClick? (): void
}
export const Button: React.FC<Props> = props => {
const {
active,
autoWidth,
primary,
flat,
fullWidth,
disabled,
round,
children,
onClick,
hint: mhint,
} = props
let { className } = props
const disabled_m = or (normalize (disabled))
className = classListMaybe (List (
Just ("btn"),
Just (orN (round) ? "btn-round" : "btn-text"),
guardReplace (orN (primary)) ("btn-primary"),
guardReplace (orN (flat)) ("btn-flat"),
guardReplace (orN (autoWidth)) ("autoWidth"),
guardReplace (orN (fullWidth)) ("fullWidth"),
guardReplace (disabled_m) ("disabled"),
guardReplace (orN (active)) ("active")
))
const btnElement = (
<div
className={className}
onClick={disabled_m ? undefined : onClick}
>
{children}
</div>
)
return maybe (btnElement)
((hint: string) => (
<TooltipHint
hint={hint}
target={btnElement}
margin={8}
/>
))
(normalize (mhint))
}
-20
View File
@@ -1,20 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { Just, Maybe } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
interface Props {
className?: string
}
export const Icon: React.FC<Props> = props => {
const { className, children } = props
return (
<div
className={classListMaybe (List (Just ("icon"), Maybe (className)))}
>
{children}
</div>
)
}
-18
View File
@@ -1,18 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { Just, Maybe } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
interface Props {
className?: string
}
export const ListView: React.FC<Props> = props => {
const { children, className } = props
return (
<ul className={classListMaybe (List (Just ("list-wrapper"), Maybe (className)))}>
{children}
</ul>
)
}
-13
View File
@@ -1,13 +0,0 @@
import * as React from "react"
interface Props { }
export const ListHeader: React.FC<Props> = props => {
const { children } = props
return (
<div className="list-header">
{children}
</div>
)
}
-34
View File
@@ -1,34 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { Just, Maybe } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
import { TooltipHint } from "./TooltipHint"
interface Props {
children?: React.ReactNode
className: string
hint?: string
}
export const ListHeaderTag: React.FC<Props> = props => {
const { children, className, hint } = props
if (typeof hint === "string") {
return (
<TooltipHint
hint={hint}
target={
<div className={classListMaybe (List (Just ("has-hint"), Maybe (className)))}>
{children}
</div>
}
/>
)
}
return (
<div className={className}>
{children}
</div>
)
}
-11
View File
@@ -1,11 +0,0 @@
.page {
flex: 1 1 auto;
font: 14px/22px Alegreya Sans;
letter-spacing: 0.025em;
color: var(--text-color);
display: flex;
flex-wrap: nowrap;
position: relative;
overflow: hidden;
height: 100%;
}
-36
View File
@@ -1,36 +0,0 @@
import * as React from "react"
import { List, notNullStrUndef } from "../../../Data/List"
import { guardReplace, Just, Maybe, orN } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
import { Text } from "./Text"
export interface TabBaseProps {
className?: string
disabled?: boolean
label: string | undefined
}
export interface TabProps extends TabBaseProps {
active: boolean
onClick (): void
}
export const Tab: React.FC<TabProps> = props => {
const { active, children, className, disabled, label, onClick } = props
return (
<div
className={
classListMaybe (List (
Just ("tab"),
guardReplace (active) ("active"),
guardReplace (orN (disabled)) ("disabled"),
Maybe (className)
))
}
onClick={orN (disabled) ? undefined : onClick}
>
<Text>{notNullStrUndef (label) ? label : children}</Text>
</div>
)
}
-220
View File
@@ -1,220 +0,0 @@
.titlebar {
position: fixed;
width: 100%;
height: calc(var(--titlebar-height) + 1px);
z-index: 11000;
top: 0;
right: 0;
-webkit-app-region: drag;
padding: 1px 1px 0;
&.empty {
height: 16px;
}
--windows-titlebar-close: #e81123;
--mac-titlebar-close: #fc5753;
--mac-titlebar-close-active: #bf4744;
--mac-titlebar-close-icon: #97040c;
--mac-titlebar-close-icon-active: #5c090b;
--mac-titlebar-minimize: #fdbc40;
--mac-titlebar-minimize-active: #be8d2f;
--mac-titlebar-minimize-icon: #985712;
--mac-titlebar-minimize-icon-active: #582806;
--mac-titlebar-fullscreen: #33c748;
--mac-titlebar-fullscreen-active: #269636;
--mac-titlebar-fullscreen-icon: #0b650d;
--mac-titlebar-fullscreen-icon-active: #023002;
}
.titlebar-inner {
height: var(--titlebar-height);
width: 100%;
display: flex;
position: relative;
justify-content: flex-end;
border-bottom: 1px solid var(--separator-color-transparent);
// background: var(--background-color);
}
.platform-win32, .platform-linux {
.titlebar .titlebar-btn {
width: 46px;
height: 30px;
font: 16px/30px TDE Heroes;
color: var(--accent-color);
-webkit-app-region: no-drag;
text-align: center;
&:hover {
color: var(--headings-color);
background: var(--separator-color-transparent);
}
&.close:hover {
background: var(--windows-titlebar-close);
}
}
}
.platform-darwin .titlebar {
.titlebar-inner {
justify-content: flex-start;
align-items: center;
}
&.not-focused .titlebar-btn {
&.close, &.minimize, &.fullscreen {
background: none;
}
}
.macos-hover-area {
-webkit-app-region: no-drag;
border-radius: 6px;
margin-left: 13px;
display: flex;
&:hover .titlebar-btn, &:active .titlebar-btn {
&.close, &.minimize, &.fullscreen {
span {
&::after, &::before {
visibility: visible;
}
}
}
&.close {
background: var(--mac-titlebar-close);
}
&.minimize {
background: var(--mac-titlebar-minimize);
}
&.fullscreen {
background: var(--mac-titlebar-fullscreen);
}
}
}
.titlebar-btn {
width: 12px;
height: 12px;
font: 24px/22px TDE Heroes;
color: transparent;
-webkit-app-region: no-drag;
text-align: center;
border-radius: 50%;
margin-left: 8px;
border: 1px solid var(--separator-color-transparent);
span {
position: relative;
display: block;
overflow: hidden;
width: 100%;
height: 100%;
&::after, &::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
visibility: hidden;
}
}
&:hover {
cursor: pointer;
}
&:first-child {
margin-left: 0;
}
&.close {
background: var(--mac-titlebar-close);
span::after, span::before {
width: 8px;
height: 2px;
background: var(--mac-titlebar-close-icon);
}
span::before {
transform: rotate(-45deg);
}
span::after {
transform: rotate(45deg);
}
&:active {
background: var(--mac-titlebar-close-active);
span::after, span::before {
background: var(--mac-titlebar-close-icon-active);
}
}
}
&.minimize {
background: var(--mac-titlebar-minimize);
span::before {
width: 8px;
height: 2px;
background: var(--mac-titlebar-minimize-icon);
}
&:active {
background: var(--mac-titlebar-minimize-active);
span::before {
background: var(--mac-titlebar-minimize-icon-active);
}
}
}
&.fullscreen {
background: var(--mac-titlebar-fullscreen);
span::after, span::before {
width: 8px;
height: 2px;
}
span::before {
background: var(--mac-titlebar-fullscreen-icon);
width: 6px;
height: 6px;
}
span::after {
background: var(--mac-titlebar-fullscreen);
width: 10px;
height: 2px;
transform: rotate(45deg);
}
&:active {
background: var(--mac-titlebar-fullscreen-active);
span::before {
background: var(--mac-titlebar-fullscreen-icon-active);
}
span::after {
background: var(--mac-titlebar-fullscreen-active);
width: 10px;
height: 2px;
transform: rotate(45deg);
}
}
}
}
}
-99
View File
@@ -1,99 +0,0 @@
import * as remote from "@electron/remote"
import * as React from "react"
import { TitleBarButton } from "./TitleBarButton"
import { TitleBarWrapper } from "./TitleBarWrapper"
interface Props {
platform: string
minimize (): void
maximize (): void
restore (): void
close (): void
enterFullscreen (): void
leaveFullscreen (): void
}
const win = remote.getCurrentWindow ()
export const TitleBar: React.FC<Props> = props => {
const {
platform,
maximize,
minimize,
restore,
enterFullscreen,
leaveFullscreen,
close,
} = props
const [ isMaximized, setIsMaximized ] = React.useState (win .isMaximized ())
const [ isFullScreen, setIsFullScreen ] = React.useState (win .isFullScreen ())
const [ isFocused, setIsFocused ] = React.useState (win .isFocused ())
const handleMaximized = React.useCallback (
() => setIsMaximized (remote.getCurrentWindow () .isMaximized ()),
[]
)
const handleFullScreen = React.useCallback (
() => setIsFullScreen (remote.getCurrentWindow () .isFullScreen ()),
[]
)
const handleFocused = React.useCallback (
() => setIsFocused (remote.getCurrentWindow () .isFocused ()),
[]
)
React.useEffect (
() => {
remote.getCurrentWindow ()
.addListener ("maximize", handleMaximized)
.addListener ("unmaximize", handleMaximized)
.addListener ("enter-full-screen", handleFullScreen)
.addListener ("leave-full-screen", handleFullScreen)
.addListener ("blur", handleFocused)
.addListener ("focus", handleFocused)
return () => {
remote.getCurrentWindow ()
.removeListener ("maximize", handleMaximized)
.removeListener ("unmaximize", handleMaximized)
.removeListener ("enter-full-screen", handleFullScreen)
.removeListener ("leave-full-screen", handleFullScreen)
.removeListener ("blur", handleFocused)
.removeListener ("focus", handleFocused)
}
},
[ handleMaximized, handleFullScreen, handleFocused ]
)
if (platform === "darwin") {
return (
<TitleBarWrapper isFocused={isFocused}>
<div className="macos-hover-area">
<TitleBarButton icon="&#xE900;" onClick={close} className="close" />
<TitleBarButton icon="&#xE903;" onClick={minimize} className="minimize" />
{
isFullScreen
? <TitleBarButton icon="&#xE902;" onClick={leaveFullscreen} className="fullscreen" />
: <TitleBarButton icon="&#xE901;" onClick={enterFullscreen} className="fullscreen" />
}
</div>
</TitleBarWrapper>
)
}
return (
<TitleBarWrapper isFocused={isFocused}>
<TitleBarButton icon="&#xE903;" onClick={minimize} className="minimize" />
{isMaximized
? null
: <TitleBarButton icon="&#xE901;" onClick={maximize} className="maximize" />}
{isMaximized
? <TitleBarButton icon="&#xE902;" onClick={restore} className="restore" />
: null}
<TitleBarButton icon="&#xE900;" onClick={close} className="close" />
</TitleBarWrapper>
)
}
@@ -1,19 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { Just, Maybe } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
interface Props {
className?: string
icon: string
onClick (): void
}
export const TitleBarButton: React.FC<Props> = ({ className, icon, onClick }) => (
<div
className={classListMaybe (List (Just ("titlebar-btn"), Maybe (className)))}
onClick={onClick}
>
<span>{icon}</span>
</div>
)
@@ -1,23 +0,0 @@
import * as React from "react"
import { List } from "../../../Data/List"
import { guardReplace, Just } from "../../../Data/Maybe"
import { classListMaybe } from "../../Utilities/CSS"
interface Props {
isFocused: boolean
}
export const TitleBarWrapper: React.FC<Props> = ({ isFocused, children }) => (
<div
className={
classListMaybe (List (
Just ("titlebar"),
guardReplace (!isFocused) ("not-focused")
))
}
>
<div className="titlebar-inner">
{children}
</div>
</div>
)
-135
View File
@@ -1,135 +0,0 @@
@import "Variables";
@import "Fonts";
@import "BasicText";
* {
box-sizing: border-box;
-ms-overflow-style: none;
&:not(input):not(textarea) {
-moz-user-select: none;
-o-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
}
a {
color: var(--link);
&:hover {
color: var(--link-hover);
}
}
p {
&:first-child {
margin: 0;
}
margin: 10px 0 0;
&.featured {
font-weight: 500;
color: var(--headings-color);
}
}
.default-1-1 {
width: 100%;
}
.flex {
display: flex;
}
%label {
label {
color: #a19b8f;
letter-spacing: 0.05em;
display: block;
font: 500 12px/1 Alegreya Sans;
padding-bottom: 6px;
}
}
.smallcaps {
font: bold 13px/1 Alegreya SC;
text-transform: uppercase;
color: var(--headings-color);
}
@import "App/Views/Universal/Aside";
@import "App/Views/Universal/Avatar";
@import "App/Views/Universal/AvatarChange";
@import "App/Views/Universal/AvatarWrapper";
@import "App/Views/Universal/BorderButton";
@import "App/Views/Universal/Box";
@import "App/Views/Universal/Button";
@import "App/Views/Universal/Checkbox";
@import "App/Views/Universal/Dialog";
@import "App/Views/Universal/Dropdown";
@import "App/Views/Universal/EditText";
@import "App/Views/Universal/ErrorMessage";
@import "App/Views/Universal/Hr";
@import "App/Views/Universal/IconButton";
@import "App/Views/Universal/Label";
@import "App/Views/Universal/LabelBox";
@import "App/Views/Universal/List";
@import "App/Views/Universal/ListHeader";
@import "App/Views/Universal/Loader";
@import "App/Views/Universal/MainContent";
@import "App/Views/Universal/NumberBox";
@import "App/Views/Universal/Options";
@import "App/Views/Universal/Overlay";
@import "App/Views/Universal/Page";
@import "App/Views/Universal/Plain";
@import "App/Views/Universal/ProgressBar";
@import "App/Views/Universal/RadioButton";
@import "App/Views/Universal/RecommendedReference";
@import "App/Views/Universal/Scroll";
@import "App/Views/Universal/SegmentedControls";
@import "App/Views/Universal/Slidein";
@import "App/Views/Universal/Slider";
@import "App/Views/Universal/TextField";
@import "App/Views/Universal/InputButtonGroup";
@import "App/Views/Universal/TitleBar";
@import "App/Views/Universal/TooltipToggle";
@import "App/Views/Universal/VerticalList";
@import "App/Views/App";
@import "App/Views/Router/Router";
@import "App/Views/About/About";
@import "App/Views/Attributes/Attribute";
@import "App/Views/DisAdvantages/DisAdvantages";
@import "App/Views/Downloader/Downloader";
@import "App/Views/Heroes/Herolist";
@import "App/Views/Equipment/Belongings";
@import "App/Views/InGame/Master";
@import "App/Views/InGame/InGame";
@import "App/Views/InGame/InGameControls";
@import "App/Views/InGame/InGameTableCenter";
@import "App/Views/InGame/InGameTableHealth";
@import "App/Views/InGame/InGameTableIni";
@import "App/Views/InGame/InGameTableLeft";
@import "App/Views/InGame/InGameTableRight";
@import "App/Views/InlineWiki/WikiInfo";
@import "App/Views/NavBar/NavigationBar";
@import "App/Views/NavBar/NavigationBarSubTabs";
@import "App/Views/Pact/Pact";
@import "App/Views/Pets/Pets";
@import "App/Views/Profile/Profile";
@import "App/Views/RCPOptionSelections/RCPOptionSelections";
@import "App/Views/Rules/Rules";
@import "App/Views/Sheets/CombatSheet/CombatSheet";
@import "App/Views/Sheets/BelongingsSheet/BelongingsSheet";
@import "App/Views/Sheets/MainSheet/MainSheet";
@import "App/Views/Sheets/Sheets";
@import "App/Views/Sheets/SpellsLiturgiesSheet";
@import "App/Views/Sheets/SkillsSheet/SkillsSheet";
@import "App/Views/Skills/Skills";
@import "App/Views/Wiki/Wiki";
@import "Print";
-77
View File
@@ -1,77 +0,0 @@
:root {
--separator-color: #7a7a7a;
--separator-color-halftransparent: rgba(122, 122, 122, .3);
--secondary-text-color: rgba(122, 122, 122, .5);
--disabled-color: rgba(122, 122, 122, .3);
--separator-color-transparent: rgba(122, 122, 122, .2);
--selection-color: #79643c;
--accent-color: #826a3e;
--accent-color-intense: #5d4517;
--background-color: #111;
--background-color-transparent02: rgba(17, 17, 17, 0.2);
--background-color-transparent04: rgba(17, 17, 17, 0.4);
--background-color-transparent06: rgba(17, 17, 17, 0.6);
--background-color-transparent08: rgba(17, 17, 17, 0.8);
--headings-color: #f7f4ed;
--text-color: #7f7f7f;
--titlebar-height: 31px;
--navigationbar-height: 64px;
--important-bright-color: #fabe0a;
--important-dark-color: #c89c3d;
--common-bright-color: #0acbe6;
--common-dark-color: #005177;
--uncommon-bright-color: #ff2345;
--uncommon-dark-color: #aa1b32;
--adult-badge: #aa1b33;
--courage-color: #aa1b32;
--sagacity-color: #6b4497;
--intuition-color: #06a341;
--charisma-color: #3c3c41;
--dexterity-color: #aea000;
--agility-color: #005177;
--constitution-color: #f7f4ed;
--strength-color: #ac6000;
--default-ease: cubic-bezier(.23, 1, .32, 1);
--link: #0080b9;
--link-hover: #52abd1;
--print-border: 0.25mm solid black;
}
.theme-light {
--selection-color: #c2ad88;
--accent-color-intense: #b6a68a;
--background-color: #f0f0f0;
--background-color-transparent02: rgba(240, 240, 240, 0.2);
--background-color-transparent04: rgba(240, 240, 240, 0.4);
--background-color-transparent06: rgba(240, 240, 240, 0.6);
--background-color-transparent08: rgba(240, 240, 240, 0.8);
--headings-color: black;
--text-color: #3c3c3c;
--important-bright-color: #cac158;
--important-dark-color: #cac158;
--common-bright-color: #5398bb;
--common-dark-color: #5398bb;
--uncommon-bright-color: #c54747;
--uncommon-dark-color: #c54747;
--courage-color: #c54747;
--sagacity-color: #a85bd4;
--intuition-color: #339b5b;
--charisma-color: black;
--dexterity-color: #cac158;
--agility-color: #5398bb;
--constitution-color: white;
--strength-color: #c28e46;
// --courage-color: #d00000;
// --sagacity-color: #8100ca;
// --intuition-color: #00ba4a;
// --charisma-color: black;
// --dexterity-color: #daca00;
// --agility-color: #007eca;
// --constitution-color: white;
// --strength-color: #ea8c00;
}
.platform-darwin {
--titlebar-height: 39px;
}
+21
View File
@@ -0,0 +1,21 @@
/* eslint-disable init-declarations */
declare module "*.png" {
declare const url: string
export default url
}
declare module "*.ico" {
declare const url: string
export default url
}
declare module "*.icns" {
declare const url: string
export default url
}
declare module "*.svg" {
declare const url: string
export default url
}

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 265 KiB

Before

Width:  |  Height:  |  Size: 818 KiB

After

Width:  |  Height:  |  Size: 818 KiB

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

Before

Width:  |  Height:  |  Size: 640 B

After

Width:  |  Height:  |  Size: 640 B

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Before

Width:  |  Height:  |  Size: 519 KiB

After

Width:  |  Height:  |  Size: 519 KiB

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Some files were not shown because too many files have changed in this diff Show More