├── .env.example ├── .github └── FUNDING.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── LICENSE ├── README.md ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── functions ├── .gitignore ├── .prettierrc ├── index.js ├── package-lock.json ├── package.json └── src │ ├── delete-sector-objects.js │ └── save-entities.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public ├── icons │ ├── android-icon-144x144.png │ ├── android-icon-192x192.png │ ├── android-icon-36x36.png │ ├── android-icon-48x48.png │ ├── android-icon-72x72.png │ ├── android-icon-96x96.png │ ├── apple-icon-114x114.png │ ├── apple-icon-120x120.png │ ├── apple-icon-144x144.png │ ├── apple-icon-152x152.png │ ├── apple-icon-180x180.png │ ├── apple-icon-57x57.png │ ├── apple-icon-60x60.png │ ├── apple-icon-72x72.png │ ├── apple-icon-76x76.png │ ├── apple-icon-precomposed.png │ ├── apple-icon.png │ ├── browserconfig.xml │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon-96x96.png │ ├── favicon.ico │ ├── ms-icon-144x144.png │ ├── ms-icon-150x150.png │ ├── ms-icon-310x310.png │ └── ms-icon-70x70.png ├── index.html └── manifest.json └── src ├── changelog.json ├── components ├── app-wrapper │ ├── app-wrapper.js │ └── index.js ├── changelog │ ├── changelog.js │ ├── index.js │ └── style.scss ├── configure │ ├── configure.js │ ├── index.js │ └── style.scss ├── custom-tag-modal │ ├── custom-tag-modal.js │ ├── index.js │ ├── styles.module.scss │ ├── tag-details.js │ └── tag-form.js ├── empty-overview │ ├── empty-overview.js │ ├── index.js │ └── style.scss ├── entity-tooltips │ ├── entity-tooltips.js │ ├── index.js │ └── styles.module.scss ├── export-modal │ ├── export-modal.js │ ├── index.js │ └── style.scss ├── faction-form │ ├── faction-asset-form.js │ ├── faction-form.js │ ├── index.js │ └── styles.module.scss ├── faction-sidebar │ ├── faction-assets │ │ ├── faction-assets.js │ │ ├── index.js │ │ └── style.scss │ ├── faction-attributes │ │ ├── faction-attributes.js │ │ ├── index.js │ │ └── style.scss │ ├── faction-sidebar.js │ ├── index.js │ └── styles.module.scss ├── faction-table │ ├── faction-not-saved │ │ ├── faction-not-saved.js │ │ └── index.js │ ├── faction-table.js │ ├── index.js │ └── style.scss ├── floating-toolbar │ ├── floating-toolbar.js │ ├── index.js │ └── style.scss ├── game-routes │ ├── index.js │ ├── routes.js │ └── style.scss ├── hex-map │ ├── hex-map.js │ ├── index.js │ └── style.scss ├── home │ ├── featured │ │ ├── index.js │ │ └── style.scss │ ├── home.js │ ├── index.js │ ├── saved │ │ ├── index.js │ │ └── style.scss │ └── style.module.scss ├── login-modal │ ├── index.js │ ├── login-modal.js │ └── style.scss ├── navigation │ ├── index.js │ ├── navigation.js │ └── style.scss ├── overview-list │ ├── index.js │ ├── overview-list.js │ └── styles.module.scss ├── overview-table │ ├── index.js │ ├── overview-table.js │ └── style.scss ├── printables │ ├── condensed-printable │ │ ├── condensed-printable.js │ │ ├── index.js │ │ └── style.scss │ ├── expanded-printable │ │ ├── expanded-printable.js │ │ ├── index.js │ │ └── style.scss │ ├── map-printable │ │ ├── index.js │ │ ├── map-hex.js │ │ ├── map-printable.js │ │ └── style.scss │ └── style.scss ├── profile-modal │ ├── index.js │ ├── profile-modal.js │ └── style.scss ├── sector-expansion-modal │ ├── index.js │ ├── sector-expansion-modal.js │ └── style.module.scss ├── sector-map │ ├── error │ │ └── index.js │ ├── index.js │ ├── loading │ │ └── index.js │ ├── sector-map.js │ └── style.scss ├── sidebar-actions │ ├── action-layout │ │ ├── action-layout.js │ │ └── index.js │ ├── default-actions │ │ ├── default-actions.js │ │ └── index.js │ ├── entity-actions │ │ ├── entity-actions.js │ │ ├── index.js │ │ └── style.scss │ └── layer-actions │ │ ├── index.js │ │ ├── layer-actions.js │ │ └── style.scss ├── sidebar-entities │ ├── default-sidebar │ │ ├── default-sidebar.js │ │ ├── entity-attribute │ │ │ ├── entity-attribute.js │ │ │ └── index.js │ │ ├── entity-attributes │ │ │ ├── entity-attributes.js │ │ │ ├── index.js │ │ │ └── styles.module.scss │ │ ├── entity-edit-row │ │ │ ├── entity-edit-row.js │ │ │ ├── index.js │ │ │ └── style.scss │ │ ├── entity-link-row │ │ │ ├── entity-link-row.js │ │ │ └── index.js │ │ ├── entity-list │ │ │ ├── entity-list.js │ │ │ ├── index.js │ │ │ └── style.scss │ │ ├── entity-tags │ │ │ ├── entity-tags.js │ │ │ ├── index.js │ │ │ └── styles.module.scss │ │ ├── index.js │ │ └── styles.module.scss │ ├── layer-sidebar │ │ ├── index.js │ │ ├── layer-form │ │ │ ├── index.js │ │ │ ├── layer-form.js │ │ │ └── style.scss │ │ ├── layer-sidebar.js │ │ ├── region-row │ │ │ ├── index.js │ │ │ ├── region-row.js │ │ │ └── style.scss │ │ └── style.scss │ ├── navigation-sidebar │ │ ├── index.js │ │ ├── navigation-sidebar.js │ │ └── style.scss │ ├── note-sidebar │ │ ├── index.js │ │ ├── note-sidebar.js │ │ └── style.scss │ └── settings-sidebar │ │ ├── index.js │ │ ├── settings-sidebar.js │ │ └── style.scss ├── sidebar │ ├── index.js │ └── sidebar.js ├── star-background │ ├── index.js │ ├── star-background.js │ └── style.scss └── top-level-entity-modal │ ├── index.js │ ├── style.scss │ └── top-level-entity-modal.js ├── constants ├── asteroid-base │ ├── occupation.js │ └── situation.js ├── asteroid-belt │ ├── occupation.js │ └── situation.js ├── atmosphere.js ├── biosphere.js ├── defaults.js ├── elements.js ├── entities.js ├── export-types.js ├── faction.js ├── gas-giant-mine │ ├── occupation.js │ └── situation.js ├── language │ ├── cosmic-names.json │ ├── greek-letters.json │ └── mars-craters.json ├── locale.js ├── lodash.js ├── moon-base │ ├── occupation.js │ └── situation.js ├── orbital-ruin │ ├── occupation.js │ └── situation.js ├── population.js ├── refueling-station │ ├── occupation.js │ └── situation.js ├── research-base │ ├── occupation.js │ └── situation.js ├── space-station │ ├── occupation.js │ └── situation.js ├── tech-level.js ├── temperature.js └── world-tags.js ├── featured.json ├── index.js ├── lang ├── de.json ├── en.json ├── es.json ├── fr.json ├── he.json ├── index.js ├── pl.json ├── ru.json ├── sr.json └── sv.json ├── primitives ├── __tests__ │ └── spinner.spec.js ├── container │ ├── absolute-container │ │ ├── index.js │ │ └── style.scss │ ├── content-container │ │ ├── index.js │ │ └── style.scss │ ├── flex-container │ │ ├── index.js │ │ └── style.scss │ ├── sidebar-container │ │ ├── index.js │ │ └── style.scss │ └── sub-container │ │ ├── index.js │ │ └── style.scss ├── form │ ├── checkbox │ │ ├── index.js │ │ └── style.scss │ ├── color-picker │ │ ├── index.js │ │ └── style.scss │ ├── deletable-row │ │ ├── index.js │ │ └── style.scss │ ├── dropdown │ │ ├── index.js │ │ └── style.scss │ ├── icon-input │ │ ├── index.js │ │ └── style.scss │ ├── input │ │ ├── index.js │ │ └── style.scss │ ├── label │ │ ├── index.js │ │ └── style.scss │ └── labeled-input │ │ ├── index.js │ │ └── style.scss ├── icons │ └── dice.js ├── modal │ ├── confirm-modal │ │ └── index.js │ └── modal │ │ ├── index.js │ │ └── style.scss ├── other │ ├── basic-link │ │ ├── index.js │ │ └── style.scss │ ├── button-link │ │ └── index.js │ ├── button │ │ ├── index.js │ │ └── style.scss │ ├── collapsible-table │ │ ├── index.js │ │ └── style.scss │ ├── color-swatch │ │ ├── index.js │ │ └── styles.module.scss │ ├── item-row │ │ ├── index.js │ │ └── style.scss │ ├── labeled-item │ │ ├── index.js │ │ └── styles.module.scss │ ├── link-icon │ │ ├── index.js │ │ └── style.scss │ ├── link-row │ │ ├── index.js │ │ └── style.scss │ ├── save-footer │ │ ├── index.js │ │ └── style.scss │ ├── spinner │ │ ├── index.js │ │ └── style.scss │ └── table │ │ ├── index.js │ │ └── style.scss ├── regions │ ├── loading │ │ └── index.js │ └── star-field │ │ ├── index.js │ │ └── style.scss └── text │ ├── action-header │ ├── index.js │ └── styles.module.scss │ ├── header │ ├── index.js │ └── style.scss │ └── section-header │ ├── index.js │ ├── section-header.js │ └── style.scss ├── serviceWorker.js ├── setupTests.js ├── store ├── actions │ ├── combined.actions.js │ ├── entity.actions.js │ ├── faction.actions.js │ ├── layer.actions.js │ ├── navigation.actions.js │ ├── sector.actions.js │ ├── settings.actions.js │ ├── sidebar.actions.js │ ├── tag.actions.js │ └── user.actions.js ├── api │ ├── entity.js │ ├── faction.js │ ├── layer.js │ ├── navigation.js │ ├── tag.js │ └── user.js ├── index.js ├── localStorage.js ├── reducers │ ├── entity.reducers.js │ ├── faction.reducers.js │ ├── index.js │ ├── layer.reducers.js │ ├── navigation.reducers.js │ ├── sector.reducers.js │ ├── settings.reducers.js │ ├── sidebar.reducers.js │ ├── tag.reducers.js │ └── user.reducers.js └── selectors │ ├── base.selectors.js │ ├── entity.selectors.js │ ├── faction.selectors.js │ ├── layer.selectors.js │ ├── navigation.selectors.js │ ├── sector.selectors.js │ └── tag.selectors.js ├── styles ├── _constants.scss ├── _fonts.scss ├── _theme.scss ├── _utilities.scss ├── fonts │ ├── KillTheNoise.eot │ ├── KillTheNoise.ttf │ └── KillTheNoise.woff └── global.scss └── utils ├── __tests__ ├── common.spec.js ├── export.spec.js ├── name-generator.spec.js └── toasts.spec.js ├── canvas-helpers.js ├── common.js ├── convert-translations.js ├── effects.js ├── entity-generators ├── asteroid-base-generator.js ├── asteroid-belt-generator.js ├── black-hole-generator.js ├── common-generator.js ├── deep-space-station-generator.js ├── gas-giant-mine-generator.js ├── index.js ├── moon-base-generator.js ├── moon-generator.js ├── note-generator.js ├── orbital-ruin-generator.js ├── planet-generator.js ├── refueling-station-generator.js ├── research-base-generator.js ├── sector-generator.js ├── space-station-generator.js └── system-generator.js ├── entity.js ├── export.js ├── faction.js ├── hex ├── canvas.js ├── common.js └── generator.js ├── name-generator.js └── toasts.js /.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_API_KEY= 2 | REACT_APP_AUTH_DOMAIN= 3 | REACT_APP_DATABASE_URL= 4 | REACT_APP_PROJECT_ID= 5 | REACT_APP_STORAGE_BUCKET= 6 | REACT_APP_SENDER_ID= 7 | REACT_APP_APP_KEY= -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: sectorswithoutnumber 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | .vscode 18 | 19 | # firebase 20 | .firebaserc 21 | .firebase 22 | .env* -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16 -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | **/node_modules -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: 3 | directories: 4 | - node_modules 5 | script: 6 | - cp .env.example .env 7 | - npm run build 8 | - npm test 9 | - npm run lint -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Mitchel Pigsley 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "firestore": { 3 | "rules": "firestore.rules", 4 | "indexes": "firestore.indexes.json" 5 | }, 6 | "hosting": { 7 | "public": "build", 8 | "headers": [ 9 | { 10 | "source": "/serviceWorker.js", 11 | "headers": [{ "key": "Cache-Control", "value": "no-cache" }] 12 | } 13 | ], 14 | "rewrites": [ 15 | { 16 | "source": "**", 17 | "destination": "/index.html" 18 | } 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [] 3 | } 4 | -------------------------------------------------------------------------------- /functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /functions/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /functions/index.js: -------------------------------------------------------------------------------- 1 | const admin = require('firebase-admin'); 2 | 3 | const saveEntities = require('./src/save-entities'); 4 | const deleteSectorObjects = require('./src/delete-sector-objects'); 5 | 6 | admin.initializeApp(); 7 | 8 | exports.saveEntities = saveEntities; 9 | exports.deleteSectorObjects = deleteSectorObjects; 10 | -------------------------------------------------------------------------------- /functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "description": "Cloud Functions for Firebase", 4 | "engines": { 5 | "node": "16" 6 | }, 7 | "dependencies": { 8 | "firebase-admin": "^11.5.0", 9 | "firebase-functions": "^4.2.1", 10 | "lodash": "^4.17.21" 11 | } 12 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "src" 4 | }, 5 | "include": ["src"] 6 | } 7 | -------------------------------------------------------------------------------- /public/icons/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-144x144.png -------------------------------------------------------------------------------- /public/icons/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-192x192.png -------------------------------------------------------------------------------- /public/icons/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-36x36.png -------------------------------------------------------------------------------- /public/icons/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-48x48.png -------------------------------------------------------------------------------- /public/icons/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-72x72.png -------------------------------------------------------------------------------- /public/icons/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/android-icon-96x96.png -------------------------------------------------------------------------------- /public/icons/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-114x114.png -------------------------------------------------------------------------------- /public/icons/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-120x120.png -------------------------------------------------------------------------------- /public/icons/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-144x144.png -------------------------------------------------------------------------------- /public/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /public/icons/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-180x180.png -------------------------------------------------------------------------------- /public/icons/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-57x57.png -------------------------------------------------------------------------------- /public/icons/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-60x60.png -------------------------------------------------------------------------------- /public/icons/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-72x72.png -------------------------------------------------------------------------------- /public/icons/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-76x76.png -------------------------------------------------------------------------------- /public/icons/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon-precomposed.png -------------------------------------------------------------------------------- /public/icons/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/apple-icon.png -------------------------------------------------------------------------------- /public/icons/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /public/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/icons/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/favicon-96x96.png -------------------------------------------------------------------------------- /public/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/favicon.ico -------------------------------------------------------------------------------- /public/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /public/icons/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/ms-icon-150x150.png -------------------------------------------------------------------------------- /public/icons/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/ms-icon-310x310.png -------------------------------------------------------------------------------- /public/icons/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mpigsley/sectors-without-number/2c70c55ed72b538d8390668a9076b7ae78da2d61/public/icons/ms-icon-70x70.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sectors Without Number", 3 | "short_name": "Sector Gen", 4 | "start_url": "./index.html", 5 | "display": "standalone", 6 | "theme_color": "#303E4F", 7 | "background_color": "#DBDBDB", 8 | "icons": [ 9 | { 10 | "src": "icons/android-icon-36x36.png", 11 | "sizes": "36x36", 12 | "type": "image/png", 13 | "density": "0.75" 14 | }, 15 | { 16 | "src": "icons/android-icon-48x48.png", 17 | "sizes": "48x48", 18 | "type": "image/png", 19 | "density": "1.0" 20 | }, 21 | { 22 | "src": "icons/android-icon-72x72.png", 23 | "sizes": "72x72", 24 | "type": "image/png", 25 | "density": "1.5" 26 | }, 27 | { 28 | "src": "icons/android-icon-96x96.png", 29 | "sizes": "96x96", 30 | "type": "image/png", 31 | "density": "2.0" 32 | }, 33 | { 34 | "src": "icons/android-icon-144x144.png", 35 | "sizes": "144x144", 36 | "type": "image/png", 37 | "density": "3.0" 38 | }, 39 | { 40 | "src": "icons/android-icon-192x192.png", 41 | "sizes": "192x192", 42 | "type": "image/png", 43 | "density": "4.0" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/components/app-wrapper/app-wrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ReduxToastr from 'react-redux-toastr'; 4 | import { IntlProvider } from 'react-intl'; 5 | 6 | import LoginModal from 'components/login-modal'; 7 | import CustomTagModal from 'components/custom-tag-modal'; 8 | 9 | export default function AppWrapper({ children, userLocale, locale, location }) { 10 | return ( 11 | 12 | <> 13 | 14 | 15 | 16 | {React.Children.map(children, child => 17 | React.cloneElement(child, { location }), 18 | )} 19 | 20 | 21 | ); 22 | } 23 | 24 | AppWrapper.propTypes = { 25 | children: PropTypes.node.isRequired, 26 | userLocale: PropTypes.string.isRequired, 27 | locale: PropTypes.shape().isRequired, 28 | location: PropTypes.shape().isRequired, 29 | }; 30 | -------------------------------------------------------------------------------- /src/components/app-wrapper/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { 4 | userLocaleSelector, 5 | userModelLocaleSelector, 6 | routerLocationSelector, 7 | } from 'store/selectors/base.selectors'; 8 | 9 | import AppWrapper from './app-wrapper'; 10 | 11 | const mapStateToProps = state => ({ 12 | userLocale: userModelLocaleSelector(state), 13 | locale: userLocaleSelector(state), 14 | location: routerLocationSelector(state), 15 | }); 16 | 17 | export default connect(mapStateToProps)(AppWrapper); 18 | -------------------------------------------------------------------------------- /src/components/changelog/index.js: -------------------------------------------------------------------------------- 1 | import Changelog from './changelog'; 2 | 3 | export default Changelog; 4 | -------------------------------------------------------------------------------- /src/components/changelog/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .Changelog-Header { 4 | margin-top: 3rem; 5 | } 6 | 7 | .Changelog-Item { 8 | color: $light2; 9 | max-width: 36rem; 10 | } 11 | 12 | .Changelog-Updates { 13 | margin-bottom: 0; 14 | margin-top: 0.3rem; 15 | } 16 | 17 | .Changelog-List { 18 | margin-top: 0.3rem; 19 | } 20 | 21 | .Changelog-Change { 22 | margin-bottom: 0.2rem; 23 | } 24 | 25 | .Changelog-Date { 26 | color: $light1; 27 | font-size: 1rem; 28 | margin-left: 0.6rem; 29 | } 30 | 31 | .Changelog-Version { 32 | margin-bottom: 0.5rem; 33 | } 34 | 35 | .Changelog-ContributorLink { 36 | color: $light2; 37 | margin-left: 0.4rem; 38 | transition: color 0.2s linear; 39 | 40 | &:hover { 41 | color: $light3; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/components/configure/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import Entities from 'constants/entities'; 5 | import { 6 | isLoggedInSelector, 7 | configurationSelector, 8 | } from 'store/selectors/base.selectors'; 9 | import { generateEntity } from 'store/actions/entity.actions'; 10 | import { updateConfiguration } from 'store/actions/sector.actions'; 11 | import { openCustomTagModal } from 'store/actions/tag.actions'; 12 | import Configure from './configure'; 13 | 14 | const mapStateToProps = state => ({ 15 | isLoggedIn: isLoggedInSelector(state), 16 | ...configurationSelector(state), 17 | }); 18 | 19 | const mapDispatchToProps = (dispatch, props) => ({ 20 | openCustomTagModal: () => dispatch(openCustomTagModal()), 21 | updateConfiguration: (key, value) => 22 | dispatch(updateConfiguration(key, value)), 23 | generateSector: () => 24 | dispatch( 25 | generateEntity({ entityType: Entities.sector.key }, {}, props.intl), 26 | ), 27 | }); 28 | 29 | export default injectIntl( 30 | connect(mapStateToProps, mapDispatchToProps)(Configure), 31 | ); 32 | -------------------------------------------------------------------------------- /src/components/configure/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .Configure-PaddedButtons { 4 | margin-top: 2rem; 5 | } 6 | 7 | .Configure-Info { 8 | color: $light1; 9 | } 10 | 11 | .Configure-ManageContainer { 12 | width: 100%; 13 | } 14 | 15 | .Configure-ManageLink { 16 | color: $light1; 17 | margin-bottom: 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/components/custom-tag-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | import { injectIntl } from 'react-intl'; 4 | 5 | import { isCustomTagModalOpenSelector } from 'store/selectors/base.selectors'; 6 | import { getUsersCustomTags } from 'store/selectors/tag.selectors'; 7 | import { 8 | createTag, 9 | editTag, 10 | deleteTag, 11 | closeCustomTagModal, 12 | } from 'store/actions/tag.actions'; 13 | 14 | import CustomTagModal from './custom-tag-modal'; 15 | 16 | const mapStateToProps = createStructuredSelector({ 17 | isCustomTagModalOpen: isCustomTagModalOpenSelector, 18 | tags: getUsersCustomTags, 19 | }); 20 | 21 | export default injectIntl( 22 | connect(mapStateToProps, { 23 | createTag, 24 | editTag, 25 | deleteTag, 26 | closeCustomTagModal, 27 | })(CustomTagModal), 28 | ); 29 | -------------------------------------------------------------------------------- /src/components/custom-tag-modal/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .modal { 4 | max-width: 900px; 5 | } 6 | 7 | .innerContainer { 8 | height: 500px; 9 | } 10 | 11 | .tagsContainer { 12 | background-color: $dark3; 13 | width: 35%; 14 | } 15 | 16 | .formBtn { 17 | margin-left: 1rem; 18 | } 19 | 20 | .btnContainer { 21 | margin-top: 0.8rem; 22 | } 23 | 24 | .scrollable { 25 | height: 100%; 26 | overflow-y: auto; 27 | } 28 | 29 | .selectedRow { 30 | background-color: $dark2; 31 | } 32 | 33 | .detailsContainer { 34 | @extend .scrollable; 35 | padding-left: 2rem; 36 | flex: 1; 37 | 38 | .formHeader { 39 | margin: 1rem 0; 40 | width: 100%; 41 | } 42 | } 43 | 44 | .rowIcon { 45 | color: $light1; 46 | } 47 | 48 | .entityTypes { 49 | margin-right: 0.5rem; 50 | } 51 | 52 | .empty * { 53 | color: $light1; 54 | } 55 | 56 | .deletableRow { 57 | margin-bottom: 0.5rem; 58 | } 59 | 60 | .contentList { 61 | margin-top: 0; 62 | } 63 | -------------------------------------------------------------------------------- /src/components/empty-overview/empty-overview.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { FormattedMessage } from 'react-intl'; 4 | 5 | import Header, { HeaderType } from 'primitives/text/header'; 6 | import FlexContainer from 'primitives/container/flex-container'; 7 | import Loading from 'primitives/regions/loading'; 8 | 9 | import './style.scss'; 10 | 11 | export default function EmptyOverview({ children, isInitialized }) { 12 | let body = ; 13 | if (isInitialized) { 14 | body = ( 15 | <> 16 |
17 | 18 |
19 |
20 | {children || } 21 |
22 | 23 | ); 24 | } 25 | return ( 26 | 33 | {body} 34 | 35 | ); 36 | } 37 | 38 | EmptyOverview.propTypes = { 39 | children: PropTypes.node, 40 | isInitialized: PropTypes.bool.isRequired, 41 | }; 42 | 43 | EmptyOverview.defaultProps = { 44 | children: undefined, 45 | }; 46 | -------------------------------------------------------------------------------- /src/components/empty-overview/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { isInitializedSelector } from 'store/selectors/base.selectors'; 4 | import EmptyOverview from './empty-overview'; 5 | 6 | const mapStateToProps = state => ({ 7 | isInitialized: isInitializedSelector(state), 8 | }); 9 | 10 | export default connect(mapStateToProps)(EmptyOverview); 11 | -------------------------------------------------------------------------------- /src/components/empty-overview/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .EmptyOverview { 4 | background-color: $dark3; 5 | position: relative; 6 | } 7 | 8 | .EmptyOverview-Header1 { 9 | color: $light1; 10 | font-size: 2.5rem; 11 | margin-bottom: 0; 12 | } 13 | 14 | .EmptyOverview-Header2 { 15 | color: $light1; 16 | } 17 | -------------------------------------------------------------------------------- /src/components/entity-tooltips/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { 5 | holdKeySelector, 6 | hoverKeySelector, 7 | } from 'store/selectors/base.selectors'; 8 | import { hexLayerNameMapping } from 'store/selectors/layer.selectors'; 9 | import EntityTooltips from './entity-tooltips'; 10 | 11 | const mapStateToProps = createStructuredSelector({ 12 | holdKey: holdKeySelector, 13 | hoverKey: hoverKeySelector, 14 | hexLayerNames: hexLayerNameMapping, 15 | }); 16 | 17 | export default connect(mapStateToProps)(EntityTooltips); 18 | -------------------------------------------------------------------------------- /src/components/entity-tooltips/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .tooltip { 4 | max-width: 15rem; 5 | opacity: 0; 6 | pointer-events: none; 7 | position: absolute; 8 | transition: opacity 0.5s; 9 | z-index: 1; 10 | 11 | &--hovered { 12 | opacity: 1; 13 | } 14 | } 15 | 16 | .text { 17 | background-color: $light4; 18 | padding: 0.6rem; 19 | border-radius: 6px; 20 | transform: translateX(-50%) translateY(-100%); 21 | z-index: 1; 22 | 23 | &:after { 24 | content: ' '; 25 | position: absolute; 26 | top: 100%; 27 | left: 50%; 28 | margin-left: -5px; 29 | border-width: 5px; 30 | border-style: solid; 31 | border-color: transparent; 32 | border-top-color: $light4; 33 | } 34 | } 35 | 36 | .name { 37 | color: $dark4; 38 | font-size: 1.1rem; 39 | margin: 0; 40 | white-space: nowrap; 41 | } 42 | 43 | .key { 44 | color: $light1; 45 | font-size: 0.8rem; 46 | margin: 0 0 0.1rem 0.5rem; 47 | } 48 | 49 | .regions { 50 | margin-top: 0.4rem; 51 | text-align: center; 52 | } 53 | 54 | .layer { 55 | font-size: 0.75rem; 56 | margin-bottom: 0.3rem; 57 | 58 | &:last-of-type { 59 | margin-bottom: 0; 60 | } 61 | 62 | b { 63 | color: $dark2; 64 | line-height: 0.85rem; 65 | margin: 0; 66 | } 67 | 68 | span { 69 | color: $dark1; 70 | line-height: 0.85rem; 71 | margin: 0 0 0 0.2rem; 72 | white-space: nowrap; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/components/export-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { 6 | customTagSelector, 7 | exportTypeSelector, 8 | isExportOpenSelector, 9 | } from 'store/selectors/base.selectors'; 10 | import { 11 | getExportEntities, 12 | getCurrentSector, 13 | } from 'store/selectors/entity.selectors'; 14 | import { 15 | setEntityExport, 16 | closeExport, 17 | startPrint, 18 | } from 'store/actions/sector.actions'; 19 | 20 | import ExportModal from './export-modal'; 21 | 22 | const mapStateToProps = createStructuredSelector({ 23 | exportType: exportTypeSelector, 24 | isExportOpen: isExportOpenSelector, 25 | customTags: customTagSelector, 26 | sector: getCurrentSector, 27 | entities: getExportEntities, 28 | }); 29 | 30 | export default injectIntl( 31 | connect(mapStateToProps, { setEntityExport, closeExport, startPrint })( 32 | ExportModal, 33 | ), 34 | ); 35 | -------------------------------------------------------------------------------- /src/components/export-modal/style.scss: -------------------------------------------------------------------------------- 1 | .ExportModal-Buttons { 2 | margin-bottom: 1rem; 3 | } 4 | 5 | .ExportModal-Description { 6 | text-align: center; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/faction-form/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { push } from 'connected-react-router'; 4 | import { createStructuredSelector } from 'reselect'; 5 | 6 | import { 7 | factionFormSelector, 8 | currentEntitySelector, 9 | factionIsCreatingSelector, 10 | } from 'store/selectors/base.selectors'; 11 | import { getCurrentEntities } from 'store/selectors/entity.selectors'; 12 | import { 13 | currentFormHitPoints, 14 | isValidFactionForm, 15 | } from 'store/selectors/faction.selectors'; 16 | import { 17 | updateFaction, 18 | updateFactionAsset, 19 | createBlankAsset, 20 | submitForm, 21 | } from 'store/actions/faction.actions'; 22 | 23 | import FactionForm from './faction-form'; 24 | 25 | const mapStateToProps = createStructuredSelector({ 26 | currentFaction: currentEntitySelector, 27 | isCreating: factionIsCreatingSelector, 28 | isValid: isValidFactionForm, 29 | form: factionFormSelector, 30 | currentEntities: getCurrentEntities, 31 | hitPoints: currentFormHitPoints, 32 | }); 33 | 34 | const mapDispatchToProps = (dispatch, props) => ({ 35 | updateFaction: update => dispatch(updateFaction(update)), 36 | createBlankAsset: () => dispatch(createBlankAsset()), 37 | toRoute: route => dispatch(push(route)), 38 | updateFactionAsset: (key, update) => 39 | dispatch(updateFactionAsset(key, update)), 40 | submitForm: () => dispatch(submitForm(props.intl)), 41 | }); 42 | 43 | export default injectIntl( 44 | connect(mapStateToProps, mapDispatchToProps)(FactionForm), 45 | ); 46 | -------------------------------------------------------------------------------- /src/components/faction-form/styles.module.scss: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 0 1rem 0.4rem; 3 | } 4 | 5 | .topRow { 6 | margin-top: 0; 7 | } 8 | 9 | .loneInput { 10 | padding-bottom: 0.5rem; 11 | } 12 | 13 | .iconInput { 14 | flex: 0; 15 | } 16 | 17 | .iconLabel { 18 | display: block; 19 | margin: 0.1rem 0 0 0.2rem; 20 | } 21 | 22 | .hitPoints { 23 | width: 5rem; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/faction-assets/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { currentFactionAssets } from 'store/selectors/faction.selectors'; 5 | 6 | import FactionAssets from './faction-assets'; 7 | 8 | const mapStateToProps = createStructuredSelector({ 9 | assets: currentFactionAssets, 10 | }); 11 | 12 | export default connect(mapStateToProps)(FactionAssets); 13 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/faction-assets/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .FactionAssets-Asset { 4 | margin-bottom: 1.5rem; 5 | } 6 | 7 | .FactionAssets-Header { 8 | font-weight: 900; 9 | margin-right: 0.5rem; 10 | } 11 | 12 | .FactionAssets-Description { 13 | color: $light2; 14 | font-size: 0.9rem; 15 | } 16 | 17 | .FactionAssets-Item { 18 | margin-bottom: 0.1rem; 19 | padding: 0; 20 | } 21 | 22 | .FactionAssets-Icon { 23 | margin-right: 0.2rem; 24 | } 25 | 26 | .FactionAssets-Spacer { 27 | margin: 0 0.4rem; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/faction-attributes/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { currentEntitySelector } from 'store/selectors/base.selectors'; 5 | import { currentFactionAttributes } from 'store/selectors/faction.selectors'; 6 | 7 | import FactionAttributes from './faction-attributes'; 8 | 9 | const mapStateToProps = createStructuredSelector({ 10 | currentFaction: currentEntitySelector, 11 | attributes: currentFactionAttributes, 12 | }); 13 | 14 | export default connect(mapStateToProps)(FactionAttributes); 15 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/faction-attributes/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .FactionAttributes-Title { 4 | font-style: italic; 5 | margin-right: 0.3rem; 6 | } 7 | 8 | .FactionAttributes-TagEffect { 9 | margin-bottom: 1rem; 10 | } 11 | 12 | .FactionAttributes-Effect { 13 | font-style: italic; 14 | margin-right: 0.8rem; 15 | } 16 | 17 | .FactionAttributes-AttributeContainer { 18 | margin-bottom: 1rem; 19 | } 20 | 21 | .FactionAttributes-Attribute { 22 | align-items: center; 23 | } 24 | 25 | .FactionAttributes-Value { 26 | color: $light4; 27 | font-size: 3.5rem; 28 | line-height: 3.2rem; 29 | } 30 | 31 | .FactionAttributes-Superscript { 32 | font-size: 1.2rem; 33 | margin-left: 0.2rem; 34 | } 35 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | import { injectIntl } from 'react-intl'; 4 | 5 | import { 6 | currentSectorSelector, 7 | currentEntitySelector, 8 | } from 'store/selectors/base.selectors'; 9 | import { currentFaction } from 'store/selectors/faction.selectors'; 10 | import { removeFaction } from 'store/actions/faction.actions'; 11 | 12 | import FactionSidebar from './faction-sidebar'; 13 | 14 | const mapStateToProps = createStructuredSelector({ 15 | faction: currentFaction, 16 | currentSector: currentSectorSelector, 17 | currentFaction: currentEntitySelector, 18 | }); 19 | 20 | const mapDispatchToProps = (dispatch, props) => ({ 21 | removeFaction: () => dispatch(removeFaction(props.intl)), 22 | }); 23 | 24 | export default injectIntl( 25 | connect(mapStateToProps, mapDispatchToProps)(FactionSidebar), 26 | ); 27 | -------------------------------------------------------------------------------- /src/components/faction-sidebar/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .content { 4 | margin: 0 1rem; 5 | } 6 | 7 | .image { 8 | margin: 1rem; 9 | width: calc(100% - 2rem); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/faction-table/faction-not-saved/faction-not-saved.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { FormattedMessage } from 'react-intl'; 4 | import { Save, LogIn } from 'react-feather'; 5 | 6 | import ContentContainer from 'primitives/container/content-container'; 7 | import Header, { HeaderType } from 'primitives/text/header'; 8 | import LinkIcon from 'primitives/other/link-icon'; 9 | import Button from 'primitives/other/button'; 10 | 11 | export default function FactionNotSaved({ 12 | saveSector, 13 | openLoginModal, 14 | isLoggedIn, 15 | }) { 16 | let action; 17 | if (isLoggedIn) { 18 | action = ( 19 | 23 | ); 24 | } else { 25 | action = ( 26 | 30 | ); 31 | } 32 | return ( 33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 | {action} 41 |
42 | ); 43 | } 44 | 45 | FactionNotSaved.propTypes = { 46 | saveSector: PropTypes.func.isRequired, 47 | openLoginModal: PropTypes.func.isRequired, 48 | isLoggedIn: PropTypes.bool.isRequired, 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/faction-table/faction-not-saved/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { openLoginModal } from 'store/actions/user.actions'; 6 | import { isLoggedInSelector } from 'store/selectors/base.selectors'; 7 | import { saveSector } from 'store/actions/entity.actions'; 8 | 9 | import FactionNotSaved from './faction-not-saved'; 10 | 11 | const mapStateToProps = createStructuredSelector({ 12 | isLoggedIn: isLoggedInSelector, 13 | }); 14 | 15 | const mapDispatchToProps = (dispatch, props) => ({ 16 | saveSector: () => dispatch(saveSector(props.intl)), 17 | openLoginModal: () => dispatch(openLoginModal()), 18 | }); 19 | 20 | export default injectIntl( 21 | connect(mapStateToProps, mapDispatchToProps)(FactionNotSaved), 22 | ); 23 | -------------------------------------------------------------------------------- /src/components/faction-table/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { withRouter } from 'react-router-dom'; 3 | import { push } from 'connected-react-router'; 4 | import { createStructuredSelector } from 'reselect'; 5 | import { injectIntl } from 'react-intl'; 6 | 7 | import { 8 | isLoggedInSelector, 9 | isInitializedSelector, 10 | currentSectorSelector, 11 | currentEntitySelector, 12 | } from 'store/selectors/base.selectors'; 13 | import { 14 | currentSectorFactionTable, 15 | currentFaction, 16 | } from 'store/selectors/faction.selectors'; 17 | import { 18 | isCurrentSectorSaved, 19 | isViewingSharedSector, 20 | currentSectorIsLoading, 21 | } from 'store/selectors/sector.selectors'; 22 | import { sectorDoesNotExist } from 'store/selectors/entity.selectors'; 23 | 24 | import FactionTable from './faction-table'; 25 | 26 | const mapStateToProps = createStructuredSelector({ 27 | isInitialized: isInitializedSelector, 28 | isLoggedIn: isLoggedInSelector, 29 | isSaved: isCurrentSectorSaved, 30 | isShared: isViewingSharedSector, 31 | doesNotExist: sectorDoesNotExist, 32 | table: currentSectorFactionTable, 33 | isLoading: currentSectorIsLoading, 34 | currentSector: currentSectorSelector, 35 | currentElement: currentEntitySelector, 36 | currentFaction, 37 | }); 38 | 39 | const mapDispatchToProps = dispatch => ({ 40 | toSafeRoute: () => dispatch(push('/')), 41 | }); 42 | 43 | export default injectIntl( 44 | withRouter(connect(mapStateToProps, mapDispatchToProps)(FactionTable)), 45 | ); 46 | -------------------------------------------------------------------------------- /src/components/faction-table/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | @import '~styles/constants'; 3 | 4 | .FactionTable { 5 | display: grid; 6 | grid-template-columns: [table] auto [sidebar] 0px [end]; 7 | grid-template-rows: [header] 4rem [table] auto [end]; 8 | overflow: hidden; 9 | 10 | &--sidebarOpen { 11 | grid-template-columns: [table] auto [sidebar] $large-sidebar-width [end]; 12 | } 13 | } 14 | 15 | .FactionTable-Header { 16 | grid-column: table / sidebar; 17 | grid-row: header / table; 18 | padding: 0 2rem; 19 | } 20 | 21 | .FactionTable-Table { 22 | grid-column: table / sidebar; 23 | grid-row: table / end; 24 | overflow: auto; 25 | padding: 2rem; 26 | } 27 | 28 | .FactionTable-Sidebar { 29 | grid-column: sidebar / end; 30 | grid-row: header / end; 31 | overflow: auto; 32 | } 33 | 34 | .FactionTable-Empty { 35 | color: $light2; 36 | font-size: 1rem; 37 | margin-top: 0.5rem; 38 | } 39 | 40 | .FactionTable-ExportOption { 41 | margin-left: 1rem; 42 | } 43 | 44 | .FactionTable-Income { 45 | margin-left: 0.3rem; 46 | margin-right: 0.1rem; 47 | } 48 | 49 | .FactionTable-Relationship { 50 | margin-left: 0.5rem; 51 | 52 | &--friendly { 53 | color: $cyber; 54 | } 55 | 56 | &--hostile { 57 | color: $error; 58 | } 59 | } 60 | 61 | .FactionTable-Stealthed { 62 | margin-left: 0.5rem; 63 | } 64 | 65 | .FactionTable-Color { 66 | margin: 0 0.5rem 0 0 !important; 67 | } 68 | 69 | @media (max-width: 1200px) { 70 | .FactionTable--sidebarOpen { 71 | grid-template-columns: [table] auto [sidebar] $small-sidebar-width [end]; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/components/floating-toolbar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { withRouter } from 'react-router-dom'; 3 | import { push } from 'connected-react-router'; 4 | import { createStructuredSelector } from 'reselect'; 5 | import { injectIntl } from 'react-intl'; 6 | 7 | import { 8 | playerViewSelector, 9 | currentSectorSelector, 10 | isSharedSectorSelector, 11 | } from 'store/selectors/base.selectors'; 12 | import { 13 | isCurrentSectorSaved, 14 | isViewingSharedSector, 15 | } from 'store/selectors/sector.selectors'; 16 | import { currentSectorLayers } from 'store/selectors/layer.selectors'; 17 | import { getMapLock, getSectorLayers } from 'store/selectors/entity.selectors'; 18 | import { toggleMapLock, toggleLayer } from 'store/actions/entity.actions'; 19 | import { togglePlayerView } from 'store/actions/sector.actions'; 20 | 21 | import FloatingToolbar from './floating-toolbar'; 22 | 23 | const mapStateToProps = createStructuredSelector({ 24 | sectorId: currentSectorSelector, 25 | mapLocked: getMapLock, 26 | layers: currentSectorLayers, 27 | sectorLayers: getSectorLayers, 28 | isSharedSector: isSharedSectorSelector, 29 | isShared: isViewingSharedSector, 30 | isSaved: isCurrentSectorSaved, 31 | playerView: playerViewSelector, 32 | }); 33 | 34 | const mapDispatchToProps = dispatch => ({ 35 | toggleMapLock: () => dispatch(toggleMapLock()), 36 | togglePlayerView: () => dispatch(togglePlayerView()), 37 | toggleLayer: layer => dispatch(toggleLayer(layer)), 38 | redirectToHome: sectorId => dispatch(push(`/sector/${sectorId}`)), 39 | }); 40 | 41 | export default injectIntl( 42 | connect(mapStateToProps, mapDispatchToProps)(withRouter(FloatingToolbar)), 43 | ); 44 | -------------------------------------------------------------------------------- /src/components/game-routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Loadable from 'react-loadable'; 3 | 4 | const LoadableGameRoutes = Loadable.Map({ 5 | delay: 400, 6 | loading: () => <>, 7 | loader: { 8 | Component: () => 9 | import( 10 | /* webpackChunkName: "game-routes" */ 'components/game-routes/routes' 11 | ), 12 | }, 13 | render: (loaded, props) => { 14 | const Routes = loaded.Component.default; 15 | return ; 16 | }, 17 | }); 18 | 19 | export default function GameRoutes() { 20 | return ; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/game-routes/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | @import '~styles/constants'; 3 | 4 | .GameRoutes { 5 | display: grid; 6 | grid-template-columns: [navigation] $large-navigation-width [game] auto [end]; 7 | height: 100vh; 8 | width: 100vw; 9 | } 10 | 11 | .GameRoutes-Navigation { 12 | grid-column: navigation / game; 13 | } 14 | 15 | @media (max-width: 1500px) { 16 | .GameRoutes { 17 | grid-template-columns: [navigation] $small-navigation-width [game] auto [end]; 18 | } 19 | } 20 | 21 | @media (max-width: 700px) { 22 | .GameRoutes { 23 | grid-template-columns: auto; 24 | } 25 | } 26 | 27 | @media print { 28 | .GameRoutes { 29 | display: block; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/hex-map/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .HexMap-SVG { 4 | display: flex; 5 | 6 | &--drag { 7 | cursor: move; 8 | } 9 | } 10 | 11 | .HexMap-Container { 12 | display: flex; 13 | overflow: hidden; 14 | position: relative; 15 | } 16 | 17 | .HexMap-Message { 18 | color: $light3; 19 | max-width: 200px; 20 | } 21 | 22 | @media print { 23 | .HexMap-Container { 24 | display: none; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/components/home/featured/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | $border: 1px solid $dark1; 4 | 5 | .HomeFeatured { 6 | border: $border; 7 | border-radius: 5px; 8 | } 9 | 10 | .HomeFeatured-Title { 11 | flex: 1; 12 | font-weight: bold; 13 | line-height: initial; 14 | padding: 1rem; 15 | } 16 | 17 | .HomeFeatured-Sector, 18 | .HomeFeatured-Website, 19 | .HomeFeatured-Patreon { 20 | color: $light2; 21 | flex: 1; 22 | padding: 1rem; 23 | text-align: center; 24 | text-decoration: none; 25 | 26 | &:hover { 27 | background-color: $transparentDark; 28 | color: $light2; 29 | } 30 | } 31 | 32 | .HomeFeatured-Website { 33 | border-top: $border; 34 | } 35 | 36 | .HomeFeatured-Sector { 37 | border-right: $border; 38 | border-top: $border; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/home/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import Entities from 'constants/entities'; 5 | import { generateEntity } from 'store/actions/entity.actions'; 6 | import { getUserSectors } from 'store/selectors/sector.selectors'; 7 | 8 | import Home from './home'; 9 | 10 | const mapStateToProps = state => ({ 11 | saved: getUserSectors(state), 12 | }); 13 | 14 | const mapDispatchToProps = (dispatch, props) => ({ 15 | generateSector: () => 16 | dispatch( 17 | generateEntity({ entityType: Entities.sector.key }, {}, props.intl), 18 | ), 19 | }); 20 | 21 | export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(Home)); 22 | -------------------------------------------------------------------------------- /src/components/home/saved/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import dayjs from 'dayjs'; 3 | import PropTypes from 'prop-types'; 4 | import { Link } from 'react-router-dom'; 5 | import { FormattedMessage } from 'react-intl'; 6 | 7 | import FlexContainer from 'primitives/container/flex-container'; 8 | import Header, { HeaderType } from 'primitives/text/header'; 9 | 10 | import './style.scss'; 11 | 12 | export default function HomeSaved({ name, sector, rows, columns, created }) { 13 | let date = new Date(created); 14 | if (!created) { 15 | date = undefined; 16 | } else if (created.toDate) { 17 | date = created.toDate(); 18 | } 19 | return ( 20 | 25 | 26 |
27 | {name} 28 |
29 |

30 | 31 | : 32 | {' '} 33 | {columns}, {rows} 34 |

35 |

36 | 37 | : 38 | {' '} 39 | {dayjs(date).format('MMMM D, YYYY')} 40 |

41 | 42 |
43 | ); 44 | } 45 | 46 | HomeSaved.propTypes = { 47 | name: PropTypes.string.isRequired, 48 | sector: PropTypes.string.isRequired, 49 | rows: PropTypes.number.isRequired, 50 | columns: PropTypes.number.isRequired, 51 | created: PropTypes.shape(), 52 | }; 53 | 54 | HomeSaved.defaultProps = { 55 | created: null, 56 | }; 57 | -------------------------------------------------------------------------------- /src/components/home/saved/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | $border: 1px solid $dark1; 4 | 5 | .HomeSaved { 6 | border: $border; 7 | border-radius: 5px; 8 | } 9 | 10 | .HomeSaved-Title { 11 | font-weight: bold; 12 | line-height: initial; 13 | padding: 0 0 1rem; 14 | } 15 | 16 | .HomeSaved-Link { 17 | color: $light3; 18 | display: flex; 19 | flex: 1; 20 | flex-direction: column; 21 | padding: 0.2rem 1rem; 22 | justify-content: center; 23 | text-align: center; 24 | text-decoration: none; 25 | 26 | &:hover { 27 | background-color: $transparentDark; 28 | } 29 | } 30 | 31 | .HomeSaved-Supporting { 32 | color: $light2; 33 | margin: 0; 34 | padding-bottom: 0.5rem; 35 | } 36 | -------------------------------------------------------------------------------- /src/components/login-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { 5 | closeLoginModal, 6 | updateUserForm, 7 | googleLogin, 8 | signup, 9 | login, 10 | passwordReset, 11 | } from 'store/actions/user.actions'; 12 | import LoginModal from './login-modal'; 13 | 14 | const mapStateToProps = ({ user }) => ({ 15 | ...user.form, 16 | isLoginModalOpen: user.isLoginModalOpen, 17 | error: user.error, 18 | }); 19 | 20 | const mapDispatchToProps = (dispatch, props) => ({ 21 | googleLogin: () => dispatch(googleLogin()), 22 | signup: () => dispatch(signup(props.intl)), 23 | login: () => dispatch(login(props.intl)), 24 | closeLoginModal: () => dispatch(closeLoginModal()), 25 | updateUserForm: (key, value) => dispatch(updateUserForm(key, value)), 26 | passwordReset: () => dispatch(passwordReset(props.intl)), 27 | }); 28 | 29 | export default injectIntl( 30 | connect(mapStateToProps, mapDispatchToProps)(LoginModal), 31 | ); 32 | -------------------------------------------------------------------------------- /src/components/navigation/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { withRouter } from 'react-router-dom'; 3 | import { createStructuredSelector } from 'reselect'; 4 | import { injectIntl } from 'react-intl'; 5 | 6 | import { 7 | openLoginModal, 8 | openEditModal, 9 | logout, 10 | } from 'store/actions/user.actions'; 11 | import { 12 | isLoggedInSelector, 13 | currentSectorSelector, 14 | lastOverviewEntitySelector, 15 | } from 'store/selectors/base.selectors'; 16 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 17 | 18 | import Navigation from './navigation'; 19 | 20 | const mapStateToProps = createStructuredSelector({ 21 | isLoggedIn: isLoggedInSelector, 22 | currentSector: currentSectorSelector, 23 | isSharedSector: isViewingSharedSector, 24 | lastOverviewEntity: lastOverviewEntitySelector, 25 | }); 26 | 27 | const mapDispatchTopProps = (dispatch, props) => ({ 28 | openLoginModal: () => dispatch(openLoginModal()), 29 | openEditModal: () => dispatch(openEditModal()), 30 | logout: () => dispatch(logout(props.intl)), 31 | }); 32 | 33 | export default injectIntl( 34 | withRouter(connect(mapStateToProps, mapDispatchTopProps)(Navigation)), 35 | ); 36 | -------------------------------------------------------------------------------- /src/components/navigation/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | $margin: 1.55rem; 4 | 5 | .Navigation { 6 | background-color: $dark2; 7 | height: 100vh; 8 | width: 185px; 9 | } 10 | 11 | .Navigation-LinkContainer { 12 | width: 100%; 13 | } 14 | 15 | .Navigation-Icon { 16 | display: inline-block; 17 | margin: 0 $margin; 18 | } 19 | 20 | .Navigation-Title { 21 | display: inline-block; 22 | font-size: 16px; 23 | margin: 0 $margin 0 0; 24 | text-align: left; 25 | width: calc(100% - (1.55rem * 3) - 25px); 26 | } 27 | 28 | .Navigation-Link { 29 | color: $light3; 30 | cursor: pointer; 31 | background-color: $dark3; 32 | padding: 1.1rem 0; 33 | text-align: center; 34 | text-decoration: none; 35 | transition: color, background-color linear 0.2s; 36 | width: 100%; 37 | 38 | &:hover, 39 | &--active { 40 | background-color: $dark4; 41 | color: $light4; 42 | } 43 | 44 | &.Navigation-Login { 45 | background-color: $primary; 46 | 47 | &:hover { 48 | background-color: $primary-alt; 49 | } 50 | } 51 | } 52 | 53 | @media (max-width: 1500px) { 54 | .Navigation { 55 | width: 75px; 56 | } 57 | 58 | .Navigation-Icon { 59 | margin: auto; 60 | } 61 | 62 | .Navigation-Title { 63 | display: none; 64 | } 65 | } 66 | 67 | @media print { 68 | .Navigation { 69 | display: none; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/components/overview-list/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { push } from 'connected-react-router'; 3 | import { injectIntl } from 'react-intl'; 4 | import { withRouter } from 'react-router-dom'; 5 | import { createStructuredSelector } from 'reselect'; 6 | 7 | import { 8 | getCurrentSector, 9 | getPrintableEntities, 10 | sectorDoesNotExist, 11 | } from 'store/selectors/entity.selectors'; 12 | import { 13 | customTagSelector, 14 | currentSectorSelector, 15 | isInitializedSelector, 16 | } from 'store/selectors/base.selectors'; 17 | import { openCustomTagModal } from 'store/actions/tag.actions'; 18 | 19 | import OverviewList from './overview-list'; 20 | 21 | const mapStateToProps = createStructuredSelector({ 22 | currentSectorId: currentSectorSelector, 23 | currentSector: getCurrentSector, 24 | entities: getPrintableEntities, 25 | isInitialized: isInitializedSelector, 26 | doesNotExist: sectorDoesNotExist, 27 | customTags: customTagSelector, 28 | }); 29 | 30 | const mapDispatchToProps = dispatch => ({ 31 | toSafeRoute: () => dispatch(push('/')), 32 | openCustomTagModal: () => dispatch(openCustomTagModal()), 33 | }); 34 | 35 | export default injectIntl( 36 | withRouter(connect(mapStateToProps, mapDispatchToProps)(OverviewList)), 37 | ); 38 | -------------------------------------------------------------------------------- /src/components/overview-list/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .container { 4 | flex: 1; 5 | max-height: 100vh; 6 | max-width: 400px; 7 | min-width: 300px; 8 | } 9 | 10 | .list { 11 | overflow: auto; 12 | width: 100%; 13 | } 14 | 15 | .selectedItem { 16 | background-color: $dark3; 17 | } 18 | 19 | .arrow { 20 | color: $dark4; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/overview-table/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { 6 | getPrintableEntities, 7 | getCurrentSector, 8 | } from 'store/selectors/entity.selectors'; 9 | import { customTagSelector } from 'store/selectors/base.selectors'; 10 | import OverviewTable from './overview-table'; 11 | 12 | const mapStateToProps = createStructuredSelector({ 13 | entities: getPrintableEntities, 14 | currentSector: getCurrentSector, 15 | customTags: customTagSelector, 16 | }); 17 | 18 | export default injectIntl(connect(mapStateToProps)(OverviewTable)); 19 | -------------------------------------------------------------------------------- /src/components/overview-table/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .OverviewTable { 4 | background-color: $dark3; 5 | max-height: calc(100vh - 1.8rem); 6 | overflow: auto; 7 | padding: 0.3rem 1.5rem 1.5rem; 8 | } 9 | 10 | .OverviewTable-Link { 11 | color: $light2; 12 | transition: color linear 0.2s; 13 | 14 | &:hover { 15 | color: $light1; 16 | } 17 | } 18 | 19 | .OverviewTable-Table { 20 | font-size: 0.75rem; 21 | } 22 | 23 | .OverviewTable-FlexWrap { 24 | width: 100%; 25 | } 26 | -------------------------------------------------------------------------------- /src/components/printables/condensed-printable/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { endPrint } from 'store/actions/sector.actions'; 5 | import { getPrintableEntities } from 'store/selectors/entity.selectors'; 6 | import { customTagSelector } from 'store/selectors/base.selectors'; 7 | import CondensedPrintable from './condensed-printable'; 8 | 9 | const mapStateToProps = state => ({ 10 | entities: getPrintableEntities(state), 11 | customTags: customTagSelector(state), 12 | }); 13 | 14 | export default injectIntl( 15 | connect(mapStateToProps, { endPrint })(CondensedPrintable), 16 | ); 17 | -------------------------------------------------------------------------------- /src/components/printables/condensed-printable/style.scss: -------------------------------------------------------------------------------- 1 | .CondensedPrintable-Entity { 2 | page-break-after: always; 3 | width: 100vw; 4 | } 5 | 6 | .CondensedPrintable-EntityTitle { 7 | margin-bottom: 1rem; 8 | } 9 | 10 | .CondensedPrintable-Table { 11 | font-size: 0.8rem; 12 | width: 100%; 13 | } 14 | -------------------------------------------------------------------------------- /src/components/printables/expanded-printable/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { endPrint } from 'store/actions/sector.actions'; 5 | import { getPrintableEntities } from 'store/selectors/entity.selectors'; 6 | import { customTagSelector } from 'store/selectors/base.selectors'; 7 | 8 | import ExpendedPrintable from './expanded-printable'; 9 | 10 | const mapStateToProps = state => ({ 11 | entities: getPrintableEntities(state), 12 | customTags: customTagSelector(state), 13 | }); 14 | 15 | export default injectIntl( 16 | connect(mapStateToProps, { endPrint })(ExpendedPrintable), 17 | ); 18 | -------------------------------------------------------------------------------- /src/components/printables/expanded-printable/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .ExpandedPrintable-Entity { 4 | page-break-after: always; 5 | width: 100%; 6 | } 7 | 8 | .ExpandedPrintable-Header { 9 | border-bottom: 1px solid $light3; 10 | width: 100%; 11 | 12 | * { 13 | margin-bottom: 0.5rem; 14 | margin-left: 0.4rem; 15 | } 16 | } 17 | 18 | .ExpandedPrintable-Type { 19 | color: $light1; 20 | margin-left: 1rem; 21 | 22 | span { 23 | margin: 0; 24 | } 25 | } 26 | 27 | .ExpandedPrintable-Block { 28 | background-color: $light4; 29 | color: $dark4; 30 | margin-top: 1rem; 31 | max-width: 350px; 32 | padding: 1rem; 33 | } 34 | 35 | .ExpandedPrintable-Name { 36 | margin-right: 0.4rem; 37 | } 38 | -------------------------------------------------------------------------------- /src/components/printables/map-printable/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { getCurrentTopLevelEntities } from 'store/selectors/entity.selectors'; 5 | 6 | import MapPrintable from './map-printable'; 7 | 8 | const mapStateToProps = state => ({ 9 | topLevelEntities: getCurrentTopLevelEntities(state), 10 | }); 11 | 12 | export default injectIntl(connect(mapStateToProps)(MapPrintable)); 13 | -------------------------------------------------------------------------------- /src/components/printables/map-printable/map-printable.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import MapHex from './map-hex'; 5 | import './style.scss'; 6 | 7 | export default function MapPrintable({ viewbox, hexes, topLevelEntities }) { 8 | return ( 9 |
10 | 15 | {hexes.map(hex => ( 16 | 21 | ))} 22 | 23 |
24 | ); 25 | } 26 | 27 | MapPrintable.propTypes = { 28 | viewbox: PropTypes.string, 29 | topLevelEntities: PropTypes.shape().isRequired, 30 | hexes: PropTypes.arrayOf( 31 | PropTypes.shape({ 32 | hexKey: PropTypes.string.isRequired, 33 | }), 34 | ), 35 | }; 36 | 37 | MapPrintable.defaultProps = { 38 | viewbox: null, 39 | hexes: [], 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/printables/map-printable/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .MapPrintable-SVG { 4 | position: absolute; 5 | top: 0; 6 | left: 0; 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | .MapPrintable-Container { 12 | background-color: white; 13 | width: 100%; 14 | height: 100%; 15 | overflow: hidden; 16 | position: relative; 17 | } 18 | 19 | .MapPrintable-BlackHole { 20 | fill: $dark4; 21 | stroke: $dark4; 22 | font-size: 25px; 23 | } 24 | 25 | .MapPrintable-System { 26 | stroke: $dark4; 27 | fill: white; 28 | font-size: 25px; 29 | stroke-width: 6px; 30 | } 31 | 32 | .MapPrintable-Polygon { 33 | fill: white; 34 | stroke-width: 3px; 35 | stroke: $dark4; 36 | transition: all 0.2s; 37 | } 38 | 39 | .MapPrintable-Text { 40 | fill: $dark4; 41 | font-size: 25px; 42 | font-weight: 100; 43 | stroke: $dark4; 44 | text-anchor: middle; 45 | stroke-width: 0.6px; 46 | user-select: none; 47 | } 48 | 49 | .MapPrintable-Children { 50 | transform: translateY(25px); 51 | } 52 | 53 | .MapPrintable-Key { 54 | transform: translateY(-8px); 55 | } 56 | 57 | .MapPrintable-Name { 58 | transform: translateY(-24px); 59 | } 60 | -------------------------------------------------------------------------------- /src/components/printables/style.scss: -------------------------------------------------------------------------------- 1 | .Printable * { 2 | font-family: 'Helvetica', sans-serif !important; 3 | -webkit-print-color-adjust: exact; 4 | } 5 | 6 | .Printable-Container { 7 | display: none; 8 | width: 100vw; 9 | height: 100vh; 10 | } 11 | 12 | .Printable-EntityContainer { 13 | display: none; 14 | width: 100vw; 15 | } 16 | 17 | @media print { 18 | .Printable-Container, 19 | .Printable-EntityContainer { 20 | display: inherit; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/profile-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { 5 | updateUserForm, 6 | updateUser, 7 | closeEditModal, 8 | } from 'store/actions/user.actions'; 9 | 10 | import ProfileModal from './profile-modal'; 11 | import { 12 | userFormSelector, 13 | isUserEditModalOpenSelector, 14 | } from '../../store/selectors/base.selectors'; 15 | 16 | const mapStateToProps = state => ({ 17 | form: userFormSelector(state), 18 | isEditModalOpen: isUserEditModalOpenSelector(state), 19 | }); 20 | 21 | const mapDispatchToProps = (dispatch, props) => ({ 22 | updateUserForm: (key, value) => dispatch(updateUserForm(key, value)), 23 | updateUser: () => dispatch(updateUser(props.intl)), 24 | closeEditModal: () => dispatch(closeEditModal()), 25 | }); 26 | 27 | export default injectIntl( 28 | connect(mapStateToProps, mapDispatchToProps)(ProfileModal), 29 | ); 30 | -------------------------------------------------------------------------------- /src/components/profile-modal/style.scss: -------------------------------------------------------------------------------- 1 | .ProfileModel-Content { 2 | padding: 1rem 0; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/sector-expansion-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | import { injectIntl } from 'react-intl'; 4 | 5 | import { closeSectorExpansion } from 'store/actions/sector.actions'; 6 | import { expandSector } from 'store/actions/combined.actions'; 7 | import { getCurrentSector } from 'store/selectors/entity.selectors'; 8 | import { isSectorExpansionOpenSelector } from 'store/selectors/base.selectors'; 9 | 10 | import SectorExpansionModal from './sector-expansion-modal'; 11 | 12 | const mapStateToProps = createStructuredSelector({ 13 | isOpen: isSectorExpansionOpenSelector, 14 | sector: getCurrentSector, 15 | }); 16 | 17 | const mapDispatchToProps = (dispatch, props) => ({ 18 | closeSectorExpansion: () => dispatch(closeSectorExpansion()), 19 | expandSector: state => dispatch(expandSector(state, props.intl)), 20 | }); 21 | 22 | export default injectIntl( 23 | connect(mapStateToProps, mapDispatchToProps)(SectorExpansionModal), 24 | ); 25 | -------------------------------------------------------------------------------- /src/components/sector-expansion-modal/style.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | @import '~styles/constants'; 3 | 4 | .square { 5 | border: 1px solid $light2; 6 | height: 125px; 7 | font-size: 1.4rem; 8 | width: 125px; 9 | } 10 | 11 | .recommendation { 12 | margin-top: 0; 13 | margin-bottom: 2rem; 14 | text-align: center; 15 | } 16 | 17 | %input { 18 | text-align: center; 19 | width: 75px; 20 | } 21 | 22 | .top { 23 | @extend %input; 24 | margin-bottom: 1rem; 25 | } 26 | 27 | .bottom { 28 | @extend %input; 29 | margin-top: 1rem; 30 | } 31 | 32 | .left { 33 | @extend %input; 34 | margin-right: 1rem; 35 | } 36 | 37 | .right { 38 | @extend %input; 39 | margin-left: 1rem; 40 | } 41 | -------------------------------------------------------------------------------- /src/components/sector-map/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { push } from 'connected-react-router'; 3 | import { injectIntl } from 'react-intl'; 4 | import { withRouter } from 'react-router-dom'; 5 | import { createStructuredSelector } from 'reselect'; 6 | 7 | import Entities from 'constants/entities'; 8 | import { fetchSector } from 'store/actions/combined.actions'; 9 | import { generateEntity } from 'store/actions/entity.actions'; 10 | import { currentSectorIsLoading } from 'store/selectors/sector.selectors'; 11 | import { 12 | getCurrentTopLevelEntities, 13 | getCurrentSector, 14 | sectorDoesNotExist, 15 | } from 'store/selectors/entity.selectors'; 16 | import { 17 | renderSectorSelector, 18 | exportTypeSelector, 19 | isPrintingSelector, 20 | fetchedSectorSelector, 21 | } from 'store/selectors/base.selectors'; 22 | 23 | import SectorMap from './sector-map'; 24 | 25 | const mapStateToProps = createStructuredSelector({ 26 | doesNotExist: sectorDoesNotExist, 27 | renderSector: renderSectorSelector, 28 | sector: getCurrentSector, 29 | isLoading: currentSectorIsLoading, 30 | topLevelEntities: getCurrentTopLevelEntities, 31 | exportType: exportTypeSelector, 32 | isPrinting: isPrintingSelector, 33 | fetchedSectors: fetchedSectorSelector, 34 | }); 35 | 36 | const mapDispatchToProps = (dispatch, props) => ({ 37 | toSafeRoute: sector => dispatch(push(sector ? `/sector/${sector}` : '/')), 38 | fetchSector: () => dispatch(fetchSector()), 39 | generateSector: () => 40 | dispatch( 41 | generateEntity({ entityType: Entities.sector.key }, {}, props.intl), 42 | ), 43 | }); 44 | 45 | export default injectIntl( 46 | withRouter(connect(mapStateToProps, mapDispatchToProps)(SectorMap)), 47 | ); 48 | -------------------------------------------------------------------------------- /src/components/sector-map/loading/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Loading from 'primitives/regions/loading'; 4 | import HexMap from 'components/hex-map'; 5 | 6 | import hexGenerator from 'utils/hex/generator'; 7 | 8 | export default function SectorLoading() { 9 | const { hexes } = hexGenerator({ 10 | renderSector: false, 11 | height: window.innerHeight, 12 | width: window.innerWidth, 13 | }); 14 | 15 | return ( 16 | <> 17 | 22 | 23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/components/sector-map/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | @import '~styles/constants'; 3 | 4 | .SectorMap { 5 | display: grid; 6 | grid-template-columns: [map] auto [sidebar] $large-sidebar-width [end]; 7 | overflow: hidden; 8 | } 9 | 10 | .SectorMap-Map { 11 | position: relative; 12 | grid-column: map / sidebar; 13 | width: calc(100vw - #{$large-sidebar-width} - #{$large-navigation-width}); 14 | } 15 | 16 | .SectorMap-Sidebar { 17 | grid-column: sidebar / end; 18 | overflow: auto; 19 | } 20 | 21 | @media (max-width: 700px) { 22 | .SectorMap-Sidebar { 23 | display: none; 24 | } 25 | } 26 | 27 | @media (max-width: 1200px) { 28 | .SectorMap { 29 | grid-template-columns: [map] auto [sidebar] $small-sidebar-width [end]; 30 | } 31 | 32 | .SectorMap-Map { 33 | width: calc(100vw - #{$small-sidebar-width} - #{$small-navigation-width}); 34 | } 35 | } 36 | 37 | @media (min-width: 1200px) and (max-width: 1500px) { 38 | .SectorMap-Map { 39 | width: calc(100vw - #{$large-sidebar-width} - #{$small-navigation-width}); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/action-layout/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { currentSectorSelector } from 'store/selectors/base.selectors'; 5 | import { 6 | isCurrentSectorSaved, 7 | isViewingSharedSector, 8 | } from 'store/selectors/sector.selectors'; 9 | import { getCurrentEntityType } from 'store/selectors/entity.selectors'; 10 | 11 | import { openExport } from 'store/actions/sector.actions'; 12 | import ActionLayout from './action-layout'; 13 | 14 | const mapStateToProps = state => ({ 15 | isSaved: isCurrentSectorSaved(state), 16 | isShared: isViewingSharedSector(state), 17 | currentSector: currentSectorSelector(state), 18 | entityType: getCurrentEntityType(state), 19 | }); 20 | 21 | export default injectIntl( 22 | connect(mapStateToProps, { openExport })(ActionLayout), 23 | ); 24 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/default-actions/default-actions.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { intlShape } from 'react-intl'; 4 | 5 | import ActionLayout from 'components/sidebar-actions/action-layout'; 6 | import Entities from 'constants/entities'; 7 | 8 | export default function DefaultActions({ intl, entityType, children }) { 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | } 15 | 16 | DefaultActions.propTypes = { 17 | intl: intlShape.isRequired, 18 | entityType: PropTypes.string, 19 | children: PropTypes.node.isRequired, 20 | }; 21 | 22 | DefaultActions.defaultProps = { 23 | entityType: undefined, 24 | }; 25 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/default-actions/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { currentSectorSelector } from 'store/selectors/base.selectors'; 5 | import { 6 | isCurrentSectorSaved, 7 | isViewingSharedSector, 8 | } from 'store/selectors/sector.selectors'; 9 | import { getCurrentEntityType } from 'store/selectors/entity.selectors'; 10 | 11 | import { openExport } from 'store/actions/sector.actions'; 12 | import DefaultActions from './default-actions'; 13 | 14 | const mapStateToProps = state => ({ 15 | isSaved: isCurrentSectorSaved(state), 16 | isShared: isViewingSharedSector(state), 17 | currentSector: currentSectorSelector(state), 18 | entityType: getCurrentEntityType(state), 19 | }); 20 | 21 | export default injectIntl( 22 | connect(mapStateToProps, { openExport })(DefaultActions), 23 | ); 24 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/entity-actions/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .EntityActions-TypeHeader { 4 | color: $light1; 5 | margin-left: 0.8rem; 6 | } 7 | 8 | .EntityActions-Welcome { 9 | padding-top: 3rem; 10 | } 11 | 12 | .EntityActions-WelcomeList { 13 | color: $light1; 14 | margin: 0 1rem 0 0.5rem; 15 | } 16 | 17 | .EntityActions-WelcomeItem { 18 | margin-top: 1rem; 19 | } 20 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/layer-actions/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { push } from 'connected-react-router'; 4 | import { createStructuredSelector } from 'reselect'; 5 | 6 | import { 7 | isCurrentSectorSaved, 8 | isViewingSharedSector, 9 | } from 'store/selectors/sector.selectors'; 10 | import { 11 | currentSectorSelector, 12 | currentEntitySelector, 13 | layerIsEditingSelector, 14 | } from 'store/selectors/base.selectors'; 15 | import { 16 | currentLayer, 17 | isValidLayerForm, 18 | } from 'store/selectors/layer.selectors'; 19 | import { 20 | initializeLayerEdit, 21 | submitForm, 22 | resetForms, 23 | } from 'store/actions/layer.actions'; 24 | import { removeLayer } from 'store/actions/combined.actions'; 25 | import LayerActions from './layer-actions'; 26 | 27 | const mapStateToProps = createStructuredSelector({ 28 | isSaved: isCurrentSectorSaved, 29 | isShared: isViewingSharedSector, 30 | isValid: isValidLayerForm, 31 | layer: currentLayer, 32 | layerId: currentEntitySelector, 33 | isEditing: layerIsEditingSelector, 34 | sector: currentSectorSelector, 35 | }); 36 | 37 | const mapDispatchToProps = (dispatch, props) => ({ 38 | removeLayer: () => dispatch(removeLayer(props.intl)), 39 | initializeLayerEdit: () => dispatch(initializeLayerEdit()), 40 | submitForm: () => dispatch(submitForm(props.intl)), 41 | cancelForm: () => dispatch(resetForms()), 42 | route: to => dispatch(push(to)), 43 | }); 44 | 45 | export default injectIntl( 46 | connect(mapStateToProps, mapDispatchToProps)(LayerActions), 47 | ); 48 | -------------------------------------------------------------------------------- /src/components/sidebar-actions/layer-actions/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .LayerActions-TypeHeader { 4 | color: $light1; 5 | margin-left: 0.8rem; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-attribute/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { getCurrentEntity } from 'store/selectors/entity.selectors'; 6 | import { isSidebarEditActiveSelector } from 'store/selectors/base.selectors'; 7 | import { updateEntityInEdit } from 'store/actions/sidebar.actions'; 8 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 9 | 10 | import EntityAttribute from './entity-attribute'; 11 | 12 | const mapStateToProps = createStructuredSelector({ 13 | entity: getCurrentEntity, 14 | isSidebarEditActive: isSidebarEditActiveSelector, 15 | isShared: isViewingSharedSector, 16 | }); 17 | 18 | export default injectIntl( 19 | connect(mapStateToProps, { updateEntityInEdit })(EntityAttribute), 20 | ); 21 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-attributes/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { 6 | getCurrentEntity, 7 | getCurrentEntityId, 8 | getCurrentEntityType, 9 | getEntityAttributes, 10 | isAncestorHidden, 11 | } from 'store/selectors/entity.selectors'; 12 | import { isSidebarEditActiveSelector } from 'store/selectors/base.selectors'; 13 | import { updateEntityInEdit } from 'store/actions/sidebar.actions'; 14 | 15 | import EntityAttributes from './entity-attributes'; 16 | 17 | const mapStateToProps = createStructuredSelector({ 18 | entity: getCurrentEntity, 19 | entityId: getCurrentEntityId, 20 | entityType: getCurrentEntityType, 21 | entityAttributes: getEntityAttributes, 22 | isSidebarEditActive: isSidebarEditActiveSelector, 23 | isAncestorHidden, 24 | }); 25 | 26 | export default injectIntl( 27 | connect(mapStateToProps, { updateEntityInEdit })(EntityAttributes), 28 | ); 29 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-attributes/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .attributes { 4 | margin: 0 1rem 1rem 1rem; 5 | width: calc(100% - 2rem); 6 | } 7 | 8 | .subHeader { 9 | color: $light2; 10 | margin: 0 1rem; 11 | } 12 | 13 | .subHeaderHidden { 14 | margin: 0rem 0.1rem 0 0.5rem; 15 | } 16 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-edit-row/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { 5 | deleteChildInEdit, 6 | undoDeleteChildInEdit, 7 | updateChildInEdit, 8 | } from 'store/actions/sidebar.actions'; 9 | import { 10 | isCurrentOrAncestorHidden, 11 | getEmptyHexKeys, 12 | } from 'store/selectors/entity.selectors'; 13 | import EntityEditRow from './entity-edit-row'; 14 | 15 | const mapStateToProps = state => ({ 16 | emptyHexKeys: getEmptyHexKeys(state), 17 | isCurrentOrAncestorHidden: isCurrentOrAncestorHidden(state), 18 | }); 19 | 20 | const mapDispatchToProps = (dispatch, props) => ({ 21 | deleteChildInEdit: () => 22 | dispatch(deleteChildInEdit(props.entityType, props.entity.entityId)), 23 | undoDeleteChildInEdit: () => 24 | dispatch(undoDeleteChildInEdit(props.entityType, props.entity.entityId)), 25 | updateChildInEdit: updates => 26 | dispatch( 27 | updateChildInEdit(props.entityType, props.entity.entityId, updates), 28 | ), 29 | }); 30 | 31 | export default injectIntl( 32 | connect(mapStateToProps, mapDispatchToProps)(EntityEditRow), 33 | ); 34 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-edit-row/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .EntityEditRow { 4 | margin: 0 1rem 0.5rem; 5 | } 6 | 7 | .EntityEditRow-Action { 8 | color: $light2; 9 | cursor: pointer; 10 | margin-right: 0.5rem; 11 | transition: all 0.2s; 12 | 13 | &:hover { 14 | color: $light3; 15 | } 16 | } 17 | 18 | .EntityEditRow-Checkbox { 19 | margin-left: 0.5rem; 20 | margin-right: 0; 21 | } 22 | 23 | .EntityEditRow-Dropdown { 24 | width: 68px; 25 | margin-right: 0.5rem; 26 | } 27 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-link-row/entity-link-row.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { EyeOff } from 'react-feather'; 4 | 5 | import LinkRow from 'primitives/other/link-row'; 6 | 7 | export default function EntityLinkRow({ 8 | entity, 9 | entityType, 10 | currentSector, 11 | isCurrentOrAncestorHidden, 12 | }) { 13 | return ( 14 | 22 | ); 23 | } 24 | 25 | EntityLinkRow.propTypes = { 26 | entity: PropTypes.shape({ 27 | entityId: PropTypes.string.isRequired, 28 | name: PropTypes.string.isRequired, 29 | additional: PropTypes.string, 30 | isHidden: PropTypes.bool, 31 | }).isRequired, 32 | entityType: PropTypes.string.isRequired, 33 | currentSector: PropTypes.string.isRequired, 34 | isCurrentOrAncestorHidden: PropTypes.bool.isRequired, 35 | }; 36 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-link-row/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { currentSectorSelector } from 'store/selectors/base.selectors'; 4 | import { isCurrentOrAncestorHidden } from 'store/selectors/entity.selectors'; 5 | 6 | import EntityLinkRow from './entity-link-row'; 7 | 8 | const mapStateToProps = state => ({ 9 | currentSector: currentSectorSelector(state), 10 | isCurrentOrAncestorHidden: isCurrentOrAncestorHidden(state), 11 | }); 12 | 13 | export default connect(mapStateToProps)(EntityLinkRow); 14 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-list/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { 5 | customTagSelector, 6 | isSidebarEditActiveSelector, 7 | sidebarEditChildrenSelector, 8 | } from 'store/selectors/base.selectors'; 9 | import { createChildInEdit } from 'store/actions/sidebar.actions'; 10 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 11 | 12 | import EntityList from './entity-list'; 13 | 14 | const mapStateToProps = (state, props) => ({ 15 | isSidebarEditActive: isSidebarEditActiveSelector(state), 16 | editableEntities: sidebarEditChildrenSelector(state)[props.entityType], 17 | isShared: isViewingSharedSector(state), 18 | customTags: customTagSelector(state), 19 | }); 20 | 21 | const mapDispatchToProps = (dispatch, props) => ({ 22 | createChildInEdit: () => dispatch(createChildInEdit(props.entityType)), 23 | }); 24 | 25 | export default injectIntl( 26 | connect(mapStateToProps, mapDispatchToProps)(EntityList), 27 | ); 28 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-list/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .EntityList-SubHeader { 4 | color: $light2; 5 | margin: 0 1rem; 6 | } 7 | 8 | .EntityList-SubHeaderHidden { 9 | margin: 0rem 0.1rem 0 0.5rem; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-tags/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { 6 | getCurrentEntity, 7 | getCurrentEntityId, 8 | getCurrentEntityType, 9 | } from 'store/selectors/entity.selectors'; 10 | import { isSidebarEditActiveSelector } from 'store/selectors/base.selectors'; 11 | import { updateEntityInEdit } from 'store/actions/sidebar.actions'; 12 | import { openCustomTagModal } from 'store/actions/tag.actions'; 13 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 14 | import { getTagsForCurrentEntity } from 'store/selectors/tag.selectors'; 15 | 16 | import EntityTags from './entity-tags'; 17 | 18 | const mapStateToProps = createStructuredSelector({ 19 | customTags: getTagsForCurrentEntity, 20 | entity: getCurrentEntity, 21 | entityId: getCurrentEntityId, 22 | entityType: getCurrentEntityType, 23 | isSidebarEditActive: isSidebarEditActiveSelector, 24 | isShared: isViewingSharedSector, 25 | }); 26 | 27 | export default injectIntl( 28 | connect(mapStateToProps, { updateEntityInEdit, openCustomTagModal })( 29 | EntityTags, 30 | ), 31 | ); 32 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/entity-tags/styles.module.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .section { 4 | margin-bottom: 1rem; 5 | } 6 | 7 | .tag { 8 | margin: 0 1rem 2rem 1rem; 9 | text-align: left; 10 | } 11 | 12 | .content { 13 | font-size: 0.9rem; 14 | font-weight: 200; 15 | color: $light2; 16 | } 17 | 18 | .contentList { 19 | margin-top: 0.1rem; 20 | } 21 | 22 | .entityTagEdit { 23 | margin: 0 1rem 0.5rem; 24 | } 25 | 26 | .dropdown { 27 | flex: 1; 28 | } 29 | 30 | .checkbox { 31 | margin-left: 0.5rem; 32 | margin-right: 0; 33 | } 34 | 35 | .subHeader { 36 | color: $light2; 37 | margin: 0 1rem; 38 | } 39 | 40 | .subHeaderHidden { 41 | margin: 0rem 0.1rem 0 0.5rem; 42 | } 43 | 44 | .customTagBtn { 45 | cursor: pointer; 46 | transition: color 0.2s linear; 47 | 48 | &:hover { 49 | color: $light2; 50 | } 51 | } 52 | 53 | .emptyTags { 54 | color: $light1; 55 | font-size: 0.9rem; 56 | margin-bottom: 1rem; 57 | } 58 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { 5 | getCurrentEntity, 6 | getCurrentEntityType, 7 | getCurrentEntityChildren, 8 | } from 'store/selectors/entity.selectors'; 9 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 10 | import { isSidebarEditActiveSelector } from 'store/selectors/base.selectors'; 11 | 12 | import DefaultSidebar from './default-sidebar'; 13 | 14 | const mapStateToProps = createStructuredSelector({ 15 | isShared: isViewingSharedSector, 16 | isSidebarEditActive: isSidebarEditActiveSelector, 17 | entity: getCurrentEntity, 18 | entityType: getCurrentEntityType, 19 | entityChildren: getCurrentEntityChildren, 20 | }); 21 | 22 | export default connect(mapStateToProps)(DefaultSidebar); 23 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/default-sidebar/styles.module.scss: -------------------------------------------------------------------------------- 1 | .entityImg { 2 | margin: 1rem; 3 | width: calc(100% - 2rem); 4 | } 5 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/layer-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { push } from 'connected-react-router'; 4 | import { createStructuredSelector } from 'reselect'; 5 | 6 | import { 7 | currentSectorSelector, 8 | currentEntitySelector, 9 | layerIsEditingSelector, 10 | layerRegionFormSelector, 11 | layerColorPickerSelector, 12 | } from 'store/selectors/base.selectors'; 13 | import { currentLayer } from 'store/selectors/layer.selectors'; 14 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 15 | import { 16 | initializeRegionForm, 17 | updateRegion, 18 | removeRegion, 19 | } from 'store/actions/layer.actions'; 20 | 21 | import LayerSidebar from './layer-sidebar'; 22 | 23 | const mapStateToProps = createStructuredSelector({ 24 | colorPicker: layerColorPickerSelector, 25 | regionForm: layerRegionFormSelector, 26 | isEditing: layerIsEditingSelector, 27 | isShared: isViewingSharedSector, 28 | sectorId: currentSectorSelector, 29 | layerId: currentEntitySelector, 30 | currentLayer, 31 | }); 32 | 33 | const mapDispatchToProps = (dispatch, props) => ({ 34 | initializeRegionForm: regionId => dispatch(initializeRegionForm(regionId)), 35 | updateRegion: (regionId, update) => dispatch(updateRegion(regionId, update)), 36 | removeRegion: regionId => dispatch(removeRegion(regionId, props.intl)), 37 | toSafeRoute: sectorId => dispatch(push(`/sector/${sectorId}`)), 38 | }); 39 | 40 | export default injectIntl( 41 | connect(mapStateToProps, mapDispatchToProps)(LayerSidebar), 42 | ); 43 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/layer-sidebar/layer-form/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { layerFormSelector } from 'store/selectors/base.selectors'; 6 | import { updateLayer } from 'store/actions/layer.actions'; 7 | 8 | import LayerForm from './layer-form'; 9 | 10 | const mapStateToProps = createStructuredSelector({ 11 | layerForm: layerFormSelector, 12 | }); 13 | 14 | export default injectIntl(connect(mapStateToProps, { updateLayer })(LayerForm)); 15 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/layer-sidebar/layer-form/style.scss: -------------------------------------------------------------------------------- 1 | .LayerForm { 2 | margin: 1rem; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/layer-sidebar/region-row/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | import { injectIntl } from 'react-intl'; 4 | 5 | import { 6 | layerRegionFormSelector, 7 | layerColorPickerSelector, 8 | layerRegionPaintSelector, 9 | } from 'store/selectors/base.selectors'; 10 | import { isViewingSharedSector } from 'store/selectors/sector.selectors'; 11 | import { 12 | updateRegionForm, 13 | initializeRegionForm, 14 | cancelRegionForm, 15 | submitRegionForm, 16 | openColorPicker, 17 | updateRegion, 18 | beginRegionPaint, 19 | closeRegionPaint, 20 | } from 'store/actions/layer.actions'; 21 | import RegionRow from './region-row'; 22 | 23 | const mapStateToProps = createStructuredSelector({ 24 | regionForm: layerRegionFormSelector, 25 | colorPicker: layerColorPickerSelector, 26 | regionPaint: layerRegionPaintSelector, 27 | isShared: isViewingSharedSector, 28 | }); 29 | 30 | const mapDispatchToProps = (dispatch, props) => ({ 31 | updateRegionForm: update => dispatch(updateRegionForm(update)), 32 | cancelRegionForm: () => dispatch(cancelRegionForm()), 33 | submitRegionForm: () => dispatch(submitRegionForm(props.intl)), 34 | openColorPicker: regionId => dispatch(openColorPicker(regionId)), 35 | initializeRegionForm: regionId => dispatch(initializeRegionForm(regionId)), 36 | updateRegion: (regionId, update) => dispatch(updateRegion(regionId, update)), 37 | beginRegionPaint: regionId => dispatch(beginRegionPaint(regionId)), 38 | closeRegionPaint: () => dispatch(closeRegionPaint()), 39 | }); 40 | 41 | export default injectIntl( 42 | connect(mapStateToProps, mapDispatchToProps)(RegionRow), 43 | ); 44 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/layer-sidebar/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .LayerSidebar { 4 | font-size: 0.9rem; 5 | margin: 1rem 0 2rem; 6 | } 7 | 8 | .LayerSidebar-Hidden { 9 | color: $light1; 10 | margin: 0 1rem 1rem; 11 | 12 | span { 13 | margin-left: 0.5rem; 14 | } 15 | } 16 | 17 | .LayerSidebar-DescriptionContainer { 18 | color: $light3; 19 | margin: 0 1rem; 20 | } 21 | 22 | .LayerSidebar-Label { 23 | font-weight: bold; 24 | } 25 | 26 | .LayerSidebar-Description { 27 | color: $light3; 28 | font-weight: 100; 29 | margin-top: 0.3rem; 30 | } 31 | 32 | .LayerSidebar-ColorHint { 33 | position: relative; 34 | 35 | &::after { 36 | border: 5px solid transparent; 37 | border-left: none; 38 | border-right-color: $light4; 39 | bottom: 0; 40 | content: ''; 41 | display: block; 42 | left: 0; 43 | margin: auto; 44 | position: absolute; 45 | height: 0; 46 | right: auto; 47 | top: 0; 48 | width: 0; 49 | } 50 | 51 | &--content { 52 | background-color: $light4; 53 | left: 5px; 54 | position: absolute; 55 | top: -16px; 56 | } 57 | 58 | &--picker { 59 | background-color: $light4; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/navigation-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { 6 | navigationSettingsSelector, 7 | isHelpOpenSelector, 8 | } from 'store/selectors/base.selectors'; 9 | import { getCurrentSectorNavigation } from 'store/selectors/navigation.selectors'; 10 | import { 11 | resetNavSettings, 12 | cancelNavigation, 13 | updateNavSettings, 14 | completeRoute, 15 | removeRoute, 16 | toggleVisibility, 17 | locateRoute, 18 | } from 'store/actions/navigation.actions'; 19 | 20 | import NavigationSidebar from './navigation-sidebar'; 21 | 22 | const mapStateToProps = createStructuredSelector({ 23 | settings: navigationSettingsSelector, 24 | isHelpOpen: isHelpOpenSelector, 25 | routes: getCurrentSectorNavigation, 26 | }); 27 | 28 | const mapDispatchToProps = (dispatch, props) => ({ 29 | resetNavSettings: () => dispatch(resetNavSettings()), 30 | cancelNavigation: () => dispatch(cancelNavigation()), 31 | updateNavSettings: (key, value) => dispatch(updateNavSettings(key, value)), 32 | completeRoute: () => dispatch(completeRoute(props.intl)), 33 | removeRoute: routeId => dispatch(removeRoute(routeId, props.intl)), 34 | toggleVisibility: routeId => dispatch(toggleVisibility(routeId, props.intl)), 35 | locateRoute: routeId => dispatch(locateRoute(routeId)), 36 | }); 37 | 38 | export default injectIntl( 39 | connect(mapStateToProps, mapDispatchToProps)(NavigationSidebar), 40 | ); 41 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/navigation-sidebar/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .NavigationSidebar-Options { 4 | margin-top: 1rem; 5 | } 6 | 7 | .NavigationSidebar { 8 | width: calc(100% - 2rem); 9 | } 10 | 11 | .NavigationSidebar-FormRow { 12 | margin: 0 1rem 1rem; 13 | width: calc(100% - 2rem); 14 | 15 | button { 16 | margin: 0; 17 | } 18 | } 19 | 20 | .NavigationSidebar-Cancel { 21 | margin-left: 0.5rem; 22 | } 23 | 24 | .NavigationSidebar-FormColumn { 25 | margin: 0.2rem 0; 26 | } 27 | 28 | .NavigationSidebar-NavItem { 29 | padding: 0 1rem; 30 | } 31 | 32 | .NavigationSidebar-SubHeader { 33 | color: $light2; 34 | margin: 0 1rem; 35 | } 36 | 37 | .NavigationSidebar-Delete { 38 | color: $light2; 39 | cursor: pointer; 40 | margin-right: 0.5rem; 41 | transition: all 0.2s; 42 | 43 | &:hover { 44 | color: $light3; 45 | } 46 | } 47 | 48 | .NavigationSidebar-Checkbox { 49 | margin-left: 0.5rem; 50 | margin-right: 0; 51 | } 52 | 53 | .NavigationSidebar-SubHeaderHidden { 54 | margin: 0rem 0.1rem 0 0.5rem; 55 | } 56 | 57 | .NavigationSidebar-Additional { 58 | color: $light1; 59 | font-size: 0.8rem; 60 | margin: 0 0.5rem; 61 | } 62 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/note-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { 5 | getCurrentEntity, 6 | isAncestorHidden, 7 | } from 'store/selectors/entity.selectors'; 8 | import { isSidebarEditActiveSelector } from 'store/selectors/base.selectors'; 9 | import { updateEntityInEdit } from 'store/actions/sidebar.actions'; 10 | 11 | import NoteSidebar from './note-sidebar'; 12 | 13 | const mapStateToProps = state => ({ 14 | note: getCurrentEntity(state), 15 | isSidebarEditActive: isSidebarEditActiveSelector(state), 16 | isAncestorHidden: isAncestorHidden(state), 17 | }); 18 | 19 | export default injectIntl( 20 | connect(mapStateToProps, { updateEntityInEdit })(NoteSidebar), 21 | ); 22 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/note-sidebar/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .NoteSidebar-Display { 4 | color: $light2; 5 | padding: 1rem; 6 | 7 | a { 8 | color: $light1; 9 | transition: linear color 0.2s; 10 | 11 | &:hover { 12 | color: $light4; 13 | } 14 | } 15 | 16 | img { 17 | max-width: 100%; 18 | } 19 | 20 | code { 21 | background: $dark1; 22 | } 23 | 24 | table { 25 | max-width: 100%; 26 | } 27 | 28 | blockquote { 29 | border-left: 3px solid $light1; 30 | color: $light1; 31 | margin-left: 1rem; 32 | padding-left: 1rem; 33 | } 34 | } 35 | 36 | .NoteSidebar-Attributes { 37 | margin: 1rem; 38 | width: calc(100% - 2rem); 39 | } 40 | 41 | .NoteSidebar-Attribute { 42 | color: $light2; 43 | font-size: 0.9rem; 44 | padding: 0.2rem 0; 45 | } 46 | 47 | .NoteSidebar-Header { 48 | padding-right: 0.5rem; 49 | width: 7rem; 50 | } 51 | 52 | .NoteSidebar-Item { 53 | flex: 1; 54 | width: 100%; 55 | 56 | &--multiline { 57 | white-space: pre-line; 58 | } 59 | } 60 | 61 | .NoteSidebar-Textarea { 62 | height: 100%; 63 | resize: none; 64 | } 65 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/settings-sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | 4 | import { updateSettings } from 'store/actions/settings.actions'; 5 | import { settingsSelector } from 'store/selectors/base.selectors'; 6 | 7 | import SettingsSidebar from './settings-sidebar'; 8 | 9 | const mapStateToProps = state => ({ 10 | settings: settingsSelector(state), 11 | }); 12 | 13 | const mapDispatchToProps = { updateSettings }; 14 | 15 | export default injectIntl( 16 | connect(mapStateToProps, mapDispatchToProps)(SettingsSidebar), 17 | ); 18 | -------------------------------------------------------------------------------- /src/components/sidebar-entities/settings-sidebar/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .SettingsSidebar-Content { 4 | margin: 0 1rem; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/sidebar/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { getCurrentEntityType } from 'store/selectors/entity.selectors'; 5 | 6 | import Sidebar from './sidebar'; 7 | 8 | const mapStateToProps = createStructuredSelector({ 9 | entityType: getCurrentEntityType, 10 | }); 11 | 12 | export default connect(mapStateToProps)(Sidebar); 13 | -------------------------------------------------------------------------------- /src/components/sidebar/sidebar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import Entities from 'constants/entities'; 5 | 6 | import DefaultSidebar from 'components/sidebar-entities/default-sidebar'; 7 | import NoteSidebar from 'components/sidebar-entities/note-sidebar'; 8 | import NavigationSidebar from 'components/sidebar-entities/navigation-sidebar'; 9 | import LayerSidebar from 'components/sidebar-entities/layer-sidebar'; 10 | import SettingsSidebar from 'components/sidebar-entities/settings-sidebar'; 11 | 12 | import DefaultActions from 'components/sidebar-actions/default-actions'; 13 | import EntityActions from 'components/sidebar-actions/entity-actions'; 14 | import LayerActions from 'components/sidebar-actions/layer-actions'; 15 | 16 | const SIDEBAR_TYPE = { 17 | default: DefaultSidebar, 18 | note: NoteSidebar, 19 | navigation: NavigationSidebar, 20 | layer: LayerSidebar, 21 | settings: SettingsSidebar, 22 | }; 23 | 24 | const ACTION_TYPE = { 25 | default: DefaultActions, 26 | entity: EntityActions, 27 | layer: LayerActions, 28 | }; 29 | 30 | export default function Sidebar({ entityType }) { 31 | const SidebarContent = 32 | SIDEBAR_TYPE[Entities[entityType].sidebar] || SIDEBAR_TYPE.default; 33 | const Actions = 34 | ACTION_TYPE[Entities[entityType].action] || ACTION_TYPE.default; 35 | return ( 36 | 37 | 38 | 39 | ); 40 | } 41 | 42 | Sidebar.propTypes = { 43 | entityType: PropTypes.string, 44 | }; 45 | 46 | Sidebar.defaultProps = { 47 | entityType: undefined, 48 | }; 49 | -------------------------------------------------------------------------------- /src/components/star-background/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { createStructuredSelector } from 'reselect'; 3 | 4 | import { 5 | userUidSelector, 6 | isInitializedSelector, 7 | } from 'store/selectors/base.selectors'; 8 | 9 | import { openLoginModal } from 'store/actions/user.actions'; 10 | 11 | import StarBackground from './star-background'; 12 | 13 | const mapStateToProps = createStructuredSelector({ 14 | isInitialized: isInitializedSelector, 15 | uid: userUidSelector, 16 | }); 17 | 18 | export default connect(mapStateToProps, { openLoginModal })(StarBackground); 19 | -------------------------------------------------------------------------------- /src/components/star-background/star-background.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { FormattedMessage } from 'react-intl'; 4 | 5 | import AbsoluteContainer from 'primitives/container/absolute-container'; 6 | import StarField from 'primitives/regions/star-field'; 7 | import Button from 'primitives/other/button'; 8 | 9 | import './style.scss'; 10 | 11 | export default function StarBackground({ 12 | children, 13 | openLoginModal, 14 | isInitialized, 15 | uid, 16 | ...props 17 | }) { 18 | let loginButton = null; 19 | if (isInitialized && !uid) { 20 | loginButton = ( 21 | 24 | ); 25 | } 26 | 27 | return ( 28 | <> 29 | 30 | 31 | {children} 32 | {loginButton} 33 | 34 | 35 | ); 36 | } 37 | 38 | StarBackground.propTypes = { 39 | children: PropTypes.node.isRequired, 40 | openLoginModal: PropTypes.func.isRequired, 41 | isInitialized: PropTypes.bool.isRequired, 42 | uid: PropTypes.string, 43 | }; 44 | 45 | StarBackground.defaultProps = { 46 | uid: null, 47 | }; 48 | -------------------------------------------------------------------------------- /src/components/star-background/style.scss: -------------------------------------------------------------------------------- 1 | .StarBackground-Login { 2 | position: absolute; 3 | right: 1rem; 4 | top: 1rem; 5 | } 6 | 7 | .StarBackground-Container { 8 | overflow: auto; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/top-level-entity-modal/index.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { injectIntl } from 'react-intl'; 3 | import { createStructuredSelector } from 'reselect'; 4 | 5 | import { cancelTopLevelEntityCreate } from 'store/actions/sector.actions'; 6 | import { generateEntity } from 'store/actions/entity.actions'; 7 | import { topLevelEntityModalOpen } from 'store/selectors/entity.selectors'; 8 | import { 9 | customTagSelector, 10 | topLevelKeySelector, 11 | currentSectorSelector, 12 | } from 'store/selectors/base.selectors'; 13 | 14 | import TopLevelEntityModal from './top-level-entity-modal'; 15 | 16 | const mapStateToProps = createStructuredSelector({ 17 | topLevelKey: topLevelKeySelector, 18 | currentSector: currentSectorSelector, 19 | isOpen: topLevelEntityModalOpen, 20 | customTags: customTagSelector, 21 | }); 22 | 23 | const mapDispatchToProps = (dispatch, props) => ({ 24 | cancelTopLevelEntityCreate: () => dispatch(cancelTopLevelEntityCreate()), 25 | generateEntity: (entity, parameters) => 26 | dispatch(generateEntity(entity, parameters, props.intl)), 27 | }); 28 | 29 | export default injectIntl( 30 | connect(mapStateToProps, mapDispatchToProps)(TopLevelEntityModal), 31 | ); 32 | -------------------------------------------------------------------------------- /src/components/top-level-entity-modal/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .TopLevelEntityModal-Type { 4 | flex: 1; 5 | margin-right: 0.25rem; 6 | } 7 | 8 | .TopLevelEntityModal-Name { 9 | flex: 1; 10 | margin-left: 0.25rem; 11 | } 12 | 13 | .TopLevelEntityModal-Planet { 14 | margin-bottom: 0.3rem; 15 | } 16 | 17 | .TopLevelEntityModal-Help { 18 | margin-bottom: 0.1rem; 19 | } 20 | 21 | .TopLevelEntityModal-Delete { 22 | cursor: pointer; 23 | margin-right: 0.5rem; 24 | transition: all 0.2s; 25 | 26 | &:hover { 27 | color: $light3; 28 | } 29 | } 30 | 31 | .TopLevelEntityModal-Add { 32 | cursor: pointer; 33 | margin-top: 0.5rem; 34 | transition: all 0.2s; 35 | font-size: 0.9rem; 36 | 37 | &:hover { 38 | color: $light3; 39 | } 40 | } 41 | 42 | .TopLevelEntityModal-Generate { 43 | margin-left: 0.5rem; 44 | margin-right: 0; 45 | } 46 | 47 | .TopLevelEntityModal-Plus { 48 | margin-right: 0.6rem; 49 | } 50 | 51 | .TopLevelEntityModal-Input { 52 | flex: 1; 53 | 54 | &--padded { 55 | margin-right: 28px; 56 | } 57 | } 58 | 59 | .TopLevelEntityModal-Error { 60 | color: $error; 61 | margin-top: 1rem; 62 | font-size: 0.9rem; 63 | text-align: center; 64 | } 65 | 66 | .TopLevelEntityModal-EmptyText { 67 | text-align: center; 68 | } 69 | -------------------------------------------------------------------------------- /src/constants/asteroid-base/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | sectarians: { 6 | key: 'sectarians', 7 | name: 'entity.asteroidBase.occupation.sectarians', 8 | }, 9 | rebels: { 10 | key: 'rebels', 11 | name: 'entity.asteroidBase.occupation.rebels', 12 | }, 13 | miners: { 14 | key: 'miners', 15 | name: 'entity.asteroidBase.occupation.miners', 16 | }, 17 | prospectors: { 18 | key: 'prospectors', 19 | name: 'entity.asteroidBase.occupation.prospectors', 20 | }, 21 | pirates: { 22 | key: 'pirates', 23 | name: 'entity.asteroidBase.occupation.pirates', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/asteroid-base/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | life: { 6 | key: 'life', 7 | name: 'entity.asteroidBase.situation.life', 8 | }, 9 | asteroid: { 10 | key: 'asteroid', 11 | name: 'entity.asteroidBase.situation.asteroid', 12 | }, 13 | nasty: { 14 | key: 'nasty', 15 | name: 'entity.asteroidBase.situation.nasty', 16 | }, 17 | fighting: { 18 | key: 'fighting', 19 | name: 'entity.asteroidBase.situation.fighting', 20 | }, 21 | priceless: { 22 | key: 'priceless', 23 | name: 'entity.asteroidBase.situation.priceless', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/asteroid-belt/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | mine: { 6 | key: 'mine', 7 | name: 'entity.asteroidBelt.occupation.mine', 8 | }, 9 | drones: { 10 | key: 'drones', 11 | name: 'entity.asteroidBelt.occupation.drones', 12 | }, 13 | base: { 14 | key: 'base', 15 | name: 'entity.asteroidBelt.occupation.base', 16 | }, 17 | pirates: { 18 | key: 'pirates', 19 | name: 'entity.asteroidBelt.occupation.pirates', 20 | }, 21 | patrol: { 22 | key: 'patrol', 23 | name: 'entity.asteroidBelt.occupation.patrol', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/asteroid-belt/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | rock: { 6 | key: 'rock', 7 | name: 'entity.asteroidBelt.situation.rock', 8 | }, 9 | spy: { 10 | key: 'spy', 11 | name: 'entity.asteroidBelt.situation.spy', 12 | }, 13 | minerals: { 14 | key: 'minerals', 15 | name: 'entity.asteroidBelt.situation.minerals', 16 | }, 17 | ruins: { 18 | key: 'ruins', 19 | name: 'entity.asteroidBelt.situation.ruins', 20 | }, 21 | war: { 22 | key: 'war', 23 | name: 'entity.asteroidBelt.situation.war', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/atmosphere.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'atmosphere', 3 | key: 'atmosphere', 4 | attributes: { 5 | corrosive: { 6 | key: 'corrosive', 7 | name: 'atmosphere.corrosive', 8 | }, 9 | inert: { 10 | key: 'inert', 11 | name: 'atmosphere.inert', 12 | }, 13 | airlessThin: { 14 | key: 'airlessThin', 15 | name: 'atmosphere.airlessThin', 16 | }, 17 | breathable: { 18 | key: 'breathable', 19 | name: 'atmosphere.breathable', 20 | }, 21 | thick: { 22 | key: 'thick', 23 | name: 'atmosphere.thick', 24 | }, 25 | invasive: { 26 | key: 'invasive', 27 | name: 'atmosphere.invasive', 28 | }, 29 | corrosiveInvasive: { 30 | key: 'corrosiveInvasive', 31 | name: 'atmosphere.corrosiveInvasive', 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/constants/biosphere.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'biosphere', 3 | key: 'biosphere', 4 | attributes: { 5 | remnant: { 6 | key: 'remnant', 7 | name: 'biosphere.remnant', 8 | }, 9 | microbial: { 10 | key: 'microbial', 11 | name: 'biosphere.microbial', 12 | }, 13 | none: { 14 | key: 'none', 15 | name: 'biosphere.none', 16 | }, 17 | humanMiscible: { 18 | key: 'humanMiscible', 19 | name: 'biosphere.humanMiscible', 20 | }, 21 | immiscible: { 22 | key: 'immiscible', 23 | name: 'biosphere.immiscible', 24 | }, 25 | hybrid: { 26 | key: 'hybrid', 27 | name: 'biosphere.hybrid', 28 | }, 29 | engineered: { 30 | key: 'engineered', 31 | name: 'biosphere.engineered', 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/constants/defaults.js: -------------------------------------------------------------------------------- 1 | export const ROWS = 10; 2 | export const COLUMNS = 8; 3 | export const SECTOR_LIMIT = 10; 4 | export const MIN_DIMENSION = 5; 5 | export const MAX_DIMENSION = 20; 6 | export const LAYER_NAME_LENGTH = 40; 7 | export const DEFAULT_HEX_WIDTH = 50; // Hex width when not rendering sector 8 | export const HEX_PADDING = 2; // Pixels between hexes 9 | export const EXTRA_HEXES = 1; // Extra hexes around canvas edges 10 | export const PIXEL_BUFFER = 75; // Pixel buffer between the sector and window 11 | export const MAX_HEXES = 1250; // Number of hexagons to render before this generator is short circuited 12 | export const TARGET_COLOR_WIDTH = 11; 13 | -------------------------------------------------------------------------------- /src/constants/elements.js: -------------------------------------------------------------------------------- 1 | const faction = { 2 | key: 'faction', 3 | }; 4 | 5 | export default { 6 | faction, 7 | }; 8 | -------------------------------------------------------------------------------- /src/constants/export-types.js: -------------------------------------------------------------------------------- 1 | const ExportTypes = { 2 | condensed: { 3 | key: 'condensed', 4 | description: 'misc.printEntitiesTable', 5 | }, 6 | expanded: { 7 | key: 'expanded', 8 | description: 'misc.oneEntity', 9 | }, 10 | image: { 11 | key: 'image', 12 | description: 'misc.imageExport', 13 | }, 14 | json: { 15 | key: 'json', 16 | description: 'misc.jsonDescription', 17 | }, 18 | }; 19 | 20 | export default ExportTypes; 21 | -------------------------------------------------------------------------------- /src/constants/gas-giant-mine/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | serfs: { 6 | key: 'serfs', 7 | name: 'entity.gasGiantMine.occupation.serfs', 8 | }, 9 | robots: { 10 | key: 'robots', 11 | name: 'entity.gasGiantMine.occupation.robots', 12 | }, 13 | alien: { 14 | key: 'alien', 15 | name: 'entity.gasGiantMine.occupation.alien', 16 | }, 17 | mine: { 18 | key: 'mine', 19 | name: 'entity.gasGiantMine.occupation.mine', 20 | }, 21 | group: { 22 | key: 'group', 23 | name: 'entity.gasGiantMine.occupation.group', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/gas-giant-mine/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | things: { 6 | key: 'things', 7 | name: 'entity.gasGiantMine.situation.things', 8 | }, 9 | supplies: { 10 | key: 'supplies', 11 | name: 'entity.gasGiantMine.situation.supplies', 12 | }, 13 | revolt: { 14 | key: 'revolt', 15 | name: 'entity.gasGiantMine.situation.revolt', 16 | }, 17 | pirates: { 18 | key: 'pirates', 19 | name: 'entity.gasGiantMine.situation.pirates', 20 | }, 21 | remnants: { 22 | key: 'remnants', 23 | name: 'entity.gasGiantMine.situation.remnants', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/language/greek-letters.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Alpha", 3 | "Beta", 4 | "Gamma", 5 | "Delta", 6 | "Epsilon", 7 | "Zeta", 8 | "Eta", 9 | "Theta", 10 | "Iota", 11 | "Kappa", 12 | "Lambda", 13 | "Mu", 14 | "Nu", 15 | "Xi", 16 | "Omicron", 17 | "Pi", 18 | "Rho", 19 | "Sigma", 20 | "Tau", 21 | "Upsilon", 22 | "Phi", 23 | "Chi", 24 | "Psi", 25 | "Omega" 26 | ] 27 | -------------------------------------------------------------------------------- /src/constants/moon-base/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | researchers: { 6 | key: 'researchers', 7 | name: 'entity.moonBase.occupation.researchers', 8 | }, 9 | genius: { 10 | key: 'genius', 11 | name: 'entity.moonBase.occupation.genius', 12 | }, 13 | colony: { 14 | key: 'colony', 15 | name: 'entity.moonBase.occupation.colony', 16 | }, 17 | military: { 18 | key: 'military', 19 | name: 'entity.moonBase.occupation.military', 20 | }, 21 | miners: { 22 | key: 'miners', 23 | name: 'entity.moonBase.occupation.miners', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/moon-base/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | dark: { 6 | key: 'dark', 7 | name: 'entity.moonBase.situation.dark', 8 | }, 9 | criminals: { 10 | key: 'criminals', 11 | name: 'entity.moonBase.situation.criminals', 12 | }, 13 | plague: { 14 | key: 'plague', 15 | name: 'entity.moonBase.situation.plague', 16 | }, 17 | supplies: { 18 | key: 'supplies', 19 | name: 'entity.moonBase.situation.supplies', 20 | }, 21 | rich: { 22 | key: 'rich', 23 | name: 'entity.moonBase.situation.rich', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/orbital-ruin/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | robots: { 6 | key: 'robots', 7 | name: 'entity.orbitalRuin.occupation.robots', 8 | }, 9 | scavengers: { 10 | key: 'scavengers', 11 | name: 'entity.orbitalRuin.occupation.scavengers', 12 | }, 13 | researchers: { 14 | key: 'researchers', 15 | name: 'entity.orbitalRuin.occupation.researchers', 16 | }, 17 | military: { 18 | key: 'military', 19 | name: 'entity.orbitalRuin.occupation.military', 20 | }, 21 | heirs: { 22 | key: 'heirs', 23 | name: 'entity.orbitalRuin.occupation.heirs', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/orbital-ruin/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | awakening: { 6 | key: 'awakening', 7 | name: 'entity.orbitalRuin.situation.awakening', 8 | }, 9 | tech: { 10 | key: 'tech', 11 | name: 'entity.orbitalRuin.situation.tech', 12 | }, 13 | calamity: { 14 | key: 'calamity', 15 | name: 'entity.orbitalRuin.situation.calamity', 16 | }, 17 | secret: { 18 | key: 'secret', 19 | name: 'entity.orbitalRuin.situation.secret', 20 | }, 21 | interlopers: { 22 | key: 'interlopers', 23 | name: 'entity.orbitalRuin.situation.interlopers', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/population.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'population', 3 | key: 'population', 4 | attributes: { 5 | failed: { 6 | key: 'failed', 7 | name: 'population.failed', 8 | }, 9 | outpost: { 10 | key: 'outpost', 11 | name: 'population.outpost', 12 | }, 13 | lessThanMillion: { 14 | key: 'lessThanMillion', 15 | name: 'population.lessThanMillion', 16 | }, 17 | severalMillion: { 18 | key: 'severalMillion', 19 | name: 'population.severalMillion', 20 | }, 21 | hundredsOfMillions: { 22 | key: 'hundredsOfMillions', 23 | name: 'population.hundredsOfMillions', 24 | }, 25 | billions: { 26 | key: 'billions', 27 | name: 'population.billions', 28 | }, 29 | alien: { 30 | key: 'alien', 31 | name: 'population.alien', 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/constants/refueling-station/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | hermit: { 6 | key: 'hermit', 7 | name: 'entity.refuelingStation.occupation.hermit', 8 | }, 9 | fun: { 10 | key: 'fun', 11 | name: 'entity.refuelingStation.occupation.fun', 12 | }, 13 | minions: { 14 | key: 'minions', 15 | name: 'entity.refuelingStation.occupation.minions', 16 | }, 17 | religious: { 18 | key: 'religious', 19 | name: 'entity.refuelingStation.occupation.religious', 20 | }, 21 | vendors: { 22 | key: 'vendors', 23 | name: 'entity.refuelingStation.occupation.vendors', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/refueling-station/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | distress: { 6 | key: 'distress', 7 | name: 'entity.refuelingStation.situation.distress', 8 | }, 9 | pirates: { 10 | key: 'pirates', 11 | name: 'entity.refuelingStation.situation.pirates', 12 | }, 13 | agents: { 14 | key: 'agents', 15 | name: 'entity.refuelingStation.situation.agents', 16 | }, 17 | saboteurs: { 18 | key: 'saboteurs', 19 | name: 'entity.refuelingStation.situation.saboteurs', 20 | }, 21 | alien: { 22 | key: 'alien', 23 | name: 'entity.refuelingStation.situation.alien', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/research-base/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | experminents: { 6 | key: 'experminents', 7 | name: 'entity.researchBase.occupation.experminents', 8 | }, 9 | scientists: { 10 | key: 'scientists', 11 | name: 'entity.researchBase.occupation.scientists', 12 | }, 13 | researchers: { 14 | key: 'researchers', 15 | name: 'entity.researchBase.occupation.researchers', 16 | }, 17 | secret: { 18 | key: 'secret', 19 | name: 'entity.researchBase.occupation.secret', 20 | }, 21 | alien: { 22 | key: 'alien', 23 | name: 'entity.researchBase.occupation.alien', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/research-base/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | research: { 6 | key: 'research', 7 | name: 'entity.researchBase.situation.research', 8 | }, 9 | immoral: { 10 | key: 'immoral', 11 | name: 'entity.researchBase.situation.immoral', 12 | }, 13 | outsiders: { 14 | key: 'outsiders', 15 | name: 'entity.researchBase.situation.outsiders', 16 | }, 17 | monsters: { 18 | key: 'monsters', 19 | name: 'entity.researchBase.situation.monsters', 20 | }, 21 | tech: { 22 | key: 'tech', 23 | name: 'entity.researchBase.situation.tech', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/space-station/occupation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.occupation', 3 | key: 'occupation', 4 | attributes: { 5 | odd: { 6 | key: 'odd', 7 | name: 'entity.spaceStation.occupation.odd', 8 | }, 9 | corpses: { 10 | key: 'corpses', 11 | name: 'entity.spaceStation.occupation.corpses', 12 | }, 13 | observers: { 14 | key: 'observers', 15 | name: 'entity.spaceStation.occupation.observers', 16 | }, 17 | minions: { 18 | key: 'minions', 19 | name: 'entity.spaceStation.occupation.minions', 20 | }, 21 | scientist: { 22 | key: 'scientist', 23 | name: 'entity.spaceStation.occupation.scientist', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/space-station/situation.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'misc.situation', 3 | key: 'situation', 4 | attributes: { 5 | systems: { 6 | key: 'systems', 7 | name: 'entity.spaceStation.situation.systems', 8 | }, 9 | sabotage: { 10 | key: 'sabotage', 11 | name: 'entity.spaceStation.situation.sabotage', 12 | }, 13 | market: { 14 | key: 'market', 15 | name: 'entity.spaceStation.situation.market', 16 | }, 17 | pretech: { 18 | key: 'pretech', 19 | name: 'entity.spaceStation.situation.pretech', 20 | }, 21 | pirates: { 22 | key: 'pirates', 23 | name: 'entity.spaceStation.situation.pirates', 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/constants/tech-level.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'techLevel', 3 | key: 'techLevel', 4 | attributes: { 5 | TL0: { key: 'TL0', name: 'techLevel.0' }, 6 | TL1: { key: 'TL1', name: 'techLevel.1' }, 7 | TL2: { key: 'TL2', name: 'techLevel.2' }, 8 | TL3: { key: 'TL3', name: 'techLevel.3' }, 9 | TL4: { key: 'TL4', name: 'techLevel.4' }, 10 | 'TL4+': { key: 'TL4+', name: 'techLevel.4+' }, 11 | TL5: { key: 'TL5', name: 'techLevel.5' }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/constants/temperature.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'temperature', 3 | key: 'temperature', 4 | attributes: { 5 | frozen: { 6 | key: 'frozen', 7 | name: 'temperature.frozen', 8 | }, 9 | cold: { 10 | key: 'cold', 11 | name: 'temperature.cold', 12 | }, 13 | variableCold: { 14 | key: 'variableCold', 15 | name: 'temperature.variableCold', 16 | }, 17 | temperate: { 18 | key: 'temperate', 19 | name: 'temperature.temperate', 20 | }, 21 | variableWarm: { 22 | key: 'variableWarm', 23 | name: 'temperature.variableWarm', 24 | }, 25 | warm: { 26 | key: 'warm', 27 | name: 'temperature.warm', 28 | }, 29 | burning: { 30 | key: 'burning', 31 | name: 'temperature.burning', 32 | }, 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /src/featured.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Far Verona's Acheron Rho", 4 | "sector": "m11ZXBOt6xiJGo21EKio", 5 | "website": "http://far-verona.wikia.com/wiki/Far_Verona_Wiki", 6 | "username": "@OutrO" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /src/lang/index.js: -------------------------------------------------------------------------------- 1 | import de from './de'; 2 | import en from './en'; 3 | import fr from './fr'; 4 | import he from './he'; 5 | import sv from './sv'; 6 | 7 | export default { de, en, fr, he, sv }; 8 | -------------------------------------------------------------------------------- /src/lang/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "atmosphere": "Атмосфера", 3 | "atmosphere.corrosive": "Едкая", 4 | "atmosphere.inert": "Инертный газ", 5 | "atmosphere.airlessThin": "Разряженная или отсутствует", 6 | "atmosphere.breathable": "Пригодная для дыхания", 7 | "atmosphere.thick": "Плотная, позволяет дышать с респиратором", 8 | "atmosphere.invasive": "Инвазивная, токсичная", 9 | "atmosphere.corrosiveInvasive": "Едкая и инвазивная", 10 | "temperature": "Климат", 11 | "temperature.frozen": "Экстремально холодный", 12 | "temperature.variableCold": "Переменный, от холодного до умеренного", 13 | "temperature.cold": "Холодный", 14 | "temperature.temperate": "Умеренный", 15 | "temperature.warm": "Теплый", 16 | "temperature.variableWarm": "Переменный, от теплого до умеренного", 17 | "temperature.burning": "Экстремально жаркий", 18 | "population": "Население", 19 | "population.failed": "Неудачная колонизация", 20 | "population.outpost": "Аванпост", 21 | "population.lessThanMillion": "Менее миллиона", 22 | "population.severalMillion": "Несколько миллионов", 23 | "population.hundredsOfMillions": "Сотни миллионов", 24 | "population.billions": "Миллиарды", 25 | "population.alien": "Ксеноцивилизация", 26 | "biosphere": "Биосфера", 27 | "biosphere.remnant": "Остаточная", 28 | "biosphere.microbial": "Микроорганизмы", 29 | "biosphere.none": "Отсутствует", 30 | "biosphere.humanMiscible": "Совместимая с земной", 31 | "biosphere.immiscible": "Несовместимая с земной", 32 | "biosphere.hybrid": "Гибридная", 33 | "biosphere.engineered": "Искусственнная", 34 | "techLevel": "Технологический уровень", 35 | "techLevel.0": "ТУ0", 36 | "techLevel.1": "ТУ1", 37 | "techLevel.2": "ТУ2", 38 | "techLevel.3": "ТУ3", 39 | "techLevel.4": "ТУ4", 40 | "techLevel.4+": "ТУ5", 41 | "techLevel.5": "ТУ6" 42 | } 43 | -------------------------------------------------------------------------------- /src/lang/sr.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /src/primitives/__tests__/spinner.spec.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { shallow } from 'enzyme'; 3 | 4 | import Spinner from '../other/spinner'; 5 | 6 | describe('Spinner', () => { 7 | it('should render the 12 spinning dots', () => { 8 | const spinner = shallow(); 9 | expect(spinner.hasClass('Spinner')).toBeTruthy(); 10 | expect(spinner.children()).toHaveLength(12); 11 | }); 12 | 13 | it('should render with a default size of 30', () => { 14 | const spinner = shallow(); 15 | expect(spinner.prop('style')).toMatchObject({ width: 30, height: 30 }); 16 | }); 17 | 18 | it('should render with a custom size', () => { 19 | const size = 50; 20 | const spinner = shallow(); 21 | expect(spinner.prop('style')).toMatchObject({ width: size, height: size }); 22 | }); 23 | 24 | it('should default to light theme', () => { 25 | const spinner = shallow(); 26 | expect(spinner.hasClass('Spinner--dark')).toBeFalsy(); 27 | }); 28 | 29 | it('should set dark theme appropriately', () => { 30 | const spinner = shallow(); 31 | expect(spinner.hasClass('Spinner--dark')).toBeTruthy(); 32 | }); 33 | 34 | it('should pass through custom class name', () => { 35 | const className = 'test'; 36 | const spinner = shallow(); 37 | expect(spinner.hasClass(className)).toBeTruthy(); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/primitives/container/absolute-container/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import './style.scss'; 6 | 7 | function AbsoluteContainer({ forwardedRef, children, className, ...props }) { 8 | return ( 9 |
14 | {children} 15 |
16 | ); 17 | } 18 | 19 | AbsoluteContainer.propTypes = { 20 | forwardedRef: PropTypes.func, 21 | children: PropTypes.node.isRequired, 22 | className: PropTypes.string, 23 | }; 24 | 25 | AbsoluteContainer.defaultProps = { 26 | forwardedRef: null, 27 | className: null, 28 | }; 29 | 30 | export default React.forwardRef((props, ref) => ( 31 | 32 | )); 33 | -------------------------------------------------------------------------------- /src/primitives/container/absolute-container/style.scss: -------------------------------------------------------------------------------- 1 | .AbsoluteContainer { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | right: 0; 6 | bottom: 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/primitives/container/content-container/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import FlexContainer from '../flex-container'; 6 | 7 | import './style.scss'; 8 | 9 | export default function ContentContainer(props) { 10 | const { children, className, ...rest } = props; 11 | return ( 12 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | ContentContainer.propTypes = { 22 | children: PropTypes.node.isRequired, 23 | className: PropTypes.string, 24 | }; 25 | 26 | ContentContainer.defaultProps = { 27 | className: null, 28 | }; 29 | -------------------------------------------------------------------------------- /src/primitives/container/content-container/style.scss: -------------------------------------------------------------------------------- 1 | .ContentContainer { 2 | height: 100%; 3 | overflow-y: auto; 4 | } 5 | -------------------------------------------------------------------------------- /src/primitives/container/flex-container/style.scss: -------------------------------------------------------------------------------- 1 | .FlexContainer { 2 | display: flex; 3 | } 4 | 5 | .FlexContainer-Align { 6 | &--stretch { 7 | align-items: stretch; 8 | } 9 | &--center { 10 | align-items: center; 11 | } 12 | &--flexStart { 13 | align-items: flex-start; 14 | } 15 | &--flexEnd { 16 | align-items: flex-end; 17 | } 18 | &--baseline { 19 | align-items: baseline; 20 | } 21 | &--initial { 22 | align-items: initial; 23 | } 24 | &--inherit { 25 | align-items: inherit; 26 | } 27 | } 28 | 29 | .FlexContainer-Direction { 30 | &--row { 31 | flex-direction: row; 32 | } 33 | &--column { 34 | flex-direction: column; 35 | } 36 | } 37 | 38 | .FlexContainer-Justify { 39 | &--flexStart { 40 | justify-content: flex-start; 41 | } 42 | &--flexEnd { 43 | justify-content: flex-end; 44 | } 45 | &--center { 46 | justify-content: center; 47 | } 48 | &--spaceBetween { 49 | justify-content: space-between; 50 | } 51 | &--spaceAround { 52 | justify-content: space-around; 53 | } 54 | &--spaceEvenly { 55 | justify-content: space-evenly; 56 | } 57 | &--initial { 58 | justify-content: initial; 59 | } 60 | &--inherit { 61 | justify-content: inherit; 62 | } 63 | } 64 | 65 | .FlexContainer-Shrink { 66 | &--0 { 67 | flex-shrink: 0; 68 | } 69 | &--1 { 70 | flex-shrink: 1; 71 | } 72 | } 73 | 74 | .FlexContainer-Wrap { 75 | flex-wrap: wrap; 76 | } 77 | 78 | .FlexContainer-Scroll { 79 | overflow-y: auto; 80 | } 81 | -------------------------------------------------------------------------------- /src/primitives/container/sidebar-container/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { FormattedMessage } from 'react-intl'; 4 | 5 | import FlexContainer from 'primitives/container/flex-container'; 6 | import ActionHeader from 'primitives/text/action-header'; 7 | import ButtonLink from 'primitives/other/button-link'; 8 | 9 | import './style.scss'; 10 | 11 | export default function SidebarContainer({ title, actions, footer, children }) { 12 | return ( 13 | 14 | 15 | 16 | {children} 17 | 18 | {footer || ( 19 |
20 | 21 | 27 | 28 | 29 | 30 |
31 | )} 32 |
33 | ); 34 | } 35 | 36 | SidebarContainer.propTypes = { 37 | title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, 38 | actions: PropTypes.arrayOf( 39 | PropTypes.shape({ 40 | key: PropTypes.string.isRequired, 41 | to: PropTypes.string, 42 | }), 43 | ), 44 | children: PropTypes.node.isRequired, 45 | footer: PropTypes.node, 46 | }; 47 | 48 | SidebarContainer.defaultProps = { 49 | actions: [], 50 | footer: undefined, 51 | }; 52 | -------------------------------------------------------------------------------- /src/primitives/container/sidebar-container/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | @import '~styles/constants'; 3 | 4 | .SidebarContainer { 5 | background-color: $dark3; 6 | height: 100vh; 7 | width: $large-sidebar-width; 8 | } 9 | 10 | .SidebarContainer-Footer { 11 | padding: 0.6rem 0; 12 | border-top: 1px solid $dark2; 13 | } 14 | 15 | .SidebarContainer-Patreon { 16 | color: $cyber; 17 | } 18 | 19 | @media (max-width: 1200px) { 20 | .SidebarContainer { 21 | width: $small-sidebar-width; 22 | } 23 | } 24 | 25 | @media (max-width: 700px) { 26 | .SidebarContainer { 27 | width: 100%; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/primitives/container/sub-container/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import FlexContainer from '../flex-container'; 6 | 7 | import './style.scss'; 8 | 9 | export default function SubContainer(props) { 10 | const { children, className, noMargin, fullWidth, ...rest } = props; 11 | return ( 12 | 19 | {children} 20 | 21 | ); 22 | } 23 | 24 | SubContainer.propTypes = { 25 | children: PropTypes.node.isRequired, 26 | className: PropTypes.string, 27 | noMargin: PropTypes.bool, 28 | fullWidth: PropTypes.bool, 29 | }; 30 | 31 | SubContainer.defaultProps = { 32 | className: null, 33 | noMargin: false, 34 | fullWidth: false, 35 | }; 36 | -------------------------------------------------------------------------------- /src/primitives/container/sub-container/style.scss: -------------------------------------------------------------------------------- 1 | .SubContainer { 2 | text-align: center; 3 | 4 | &--fullWidth { width: calc(100% - 1rem) } 5 | &--margin { margin: 0 1rem; } 6 | } -------------------------------------------------------------------------------- /src/primitives/form/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import FlexContainer from 'primitives/container/flex-container'; 5 | import Input from 'primitives/form/input'; 6 | import Label from 'primitives/form/label'; 7 | 8 | import './style.scss'; 9 | 10 | export default function Checkbox({ label, value, onChange, ...rest }) { 11 | return ( 12 | 13 | 20 | 23 | 24 | ); 25 | } 26 | 27 | Checkbox.propTypes = { 28 | label: PropTypes.string.isRequired, 29 | value: PropTypes.bool.isRequired, 30 | onChange: PropTypes.func.isRequired, 31 | }; 32 | -------------------------------------------------------------------------------- /src/primitives/form/checkbox/style.scss: -------------------------------------------------------------------------------- 1 | .Checkbox { 2 | margin-top: 1.2rem; 3 | 4 | & * { 5 | margin: 0; 6 | } 7 | 8 | & input { 9 | height: 1.3rem; 10 | margin-right: 1rem; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/primitives/form/color-picker/style.scss: -------------------------------------------------------------------------------- 1 | .ColorPicker-InputContainer { 2 | padding: 1px; 3 | padding-left: 15px; 4 | border-radius: 4px; 5 | margin-right: 0.2rem; 6 | } 7 | 8 | .ColorPicker-Input { 9 | border: none; 10 | border-top-right-radius: 4px; 11 | border-bottom-right-radius: 4px; 12 | margin: 0; 13 | padding: 9.5px 6px; 14 | width: 100%; 15 | 16 | &:focus { 17 | border: none; 18 | outline: none; 19 | } 20 | } 21 | 22 | .ColorPicker-Swatch { 23 | margin: 0 0.2rem; 24 | 25 | &:last-of-type { 26 | margin-right: 0; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/primitives/form/deletable-row/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { RotateCcw, X } from 'react-feather'; 4 | 5 | import FlexContainer from 'primitives/container/flex-container'; 6 | 7 | import './style.scss'; 8 | 9 | export default function DeletableRow({ 10 | onAction, 11 | undoDelete, 12 | children, 13 | ...props 14 | }) { 15 | const Icon = undoDelete ? RotateCcw : X; 16 | return ( 17 | 18 | 19 | {children} 20 | 21 | ); 22 | } 23 | 24 | DeletableRow.propTypes = { 25 | onAction: PropTypes.func.isRequired, 26 | undoDelete: PropTypes.bool, 27 | children: PropTypes.node.isRequired, 28 | }; 29 | 30 | DeletableRow.defaultProps = { 31 | undoDelete: false, 32 | }; 33 | -------------------------------------------------------------------------------- /src/primitives/form/deletable-row/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .DeletableRow-Icon { 4 | color: $light2; 5 | cursor: pointer; 6 | margin-right: 0.5rem; 7 | transition: all 0.2s; 8 | 9 | &:hover { 10 | color: $light3; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/primitives/form/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | import Select, { Creatable } from 'react-select'; 5 | 6 | import './style.scss'; 7 | 8 | export default function Dropdown({ 9 | onItemClick, 10 | icon, 11 | dropUp, 12 | allowCreate, 13 | wrapperClassName, 14 | ...rest 15 | }) { 16 | const newProps = { 17 | promptTextCreator: label => label, 18 | ...rest, 19 | }; 20 | const DropdownComponent = allowCreate ? Creatable : Select; 21 | const Icon = icon; 22 | return ( 23 |
24 | 32 | {icon && ( 33 |
34 | 35 |
36 | )} 37 |
38 | ); 39 | } 40 | 41 | Dropdown.propTypes = { 42 | ...Select.propTypes, 43 | dropUp: PropTypes.bool, 44 | allowCreate: PropTypes.bool, 45 | wrapperClassName: PropTypes.string, 46 | onItemClick: PropTypes.func, 47 | }; 48 | 49 | Dropdown.defaultProps = { 50 | dropUp: false, 51 | allowCreate: false, 52 | wrapperClassName: null, 53 | onItemClick: () => {}, 54 | }; 55 | -------------------------------------------------------------------------------- /src/primitives/form/icon-input/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import Input from 'primitives/form/input'; 6 | 7 | import './style.scss'; 8 | 9 | const defaultFunc = () => {}; 10 | 11 | export default function IconInput({ 12 | icon, 13 | onIconClick, 14 | wrapperClassName, 15 | className, 16 | ...rest 17 | }) { 18 | const Icon = icon; 19 | return ( 20 |
21 | 28 | 29 |
30 | ); 31 | } 32 | 33 | IconInput.propTypes = { 34 | className: PropTypes.string, 35 | wrapperClassName: PropTypes.string, 36 | icon: PropTypes.func.isRequired, 37 | onIconClick: PropTypes.func, 38 | }; 39 | 40 | IconInput.defaultProps = { 41 | className: null, 42 | wrapperClassName: null, 43 | onIconClick: null, 44 | }; 45 | -------------------------------------------------------------------------------- /src/primitives/form/icon-input/style.scss: -------------------------------------------------------------------------------- 1 | @import '~styles/theme'; 2 | 3 | .IconInput { 4 | flex: 1; 5 | position: relative; 6 | width: 100%; 7 | } 8 | 9 | .IconInput-Input { 10 | padding-left: 40px; 11 | } 12 | 13 | .IconInput-Icon { 14 | color: $dark1; 15 | position: absolute; 16 | left: 12px; 17 | top: 11px; 18 | transition: all 0.2s; 19 | cursor: pointer; 20 | 21 | &:hover { 22 | color: $dark3; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/primitives/form/input/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classNames from 'classnames'; 4 | 5 | import ColorPicker from 'primitives/form/color-picker'; 6 | import Dropdown from 'primitives/form/dropdown'; 7 | 8 | import './style.scss'; 9 | 10 | export default function Input({ className, width, error, type, ...rest }) { 11 | let style = {}; 12 | if (width) { 13 | style = { width }; 14 | } 15 | if (type === 'textarea') { 16 | return ( 17 |