update: all models, core, interface, settings

This commit is contained in:
unknown 2020-07-30 05:54:11 +02:00
parent 599e706a23
commit 660d4848cb
31 changed files with 1071 additions and 306 deletions

3
.gitignore vendored
View File

@ -9,7 +9,7 @@
# production # production
/dist /dist
/.debug
# misc # misc
.DS_Store .DS_Store
@ -18,3 +18,4 @@
/src/.umi-production /src/.umi-production
/src/.umi-test /src/.umi-test
/.env.local /.env.local
.debug/4.11/.editorconfig

View File

@ -10,7 +10,13 @@ module.exports = {
DarkLogoPath: '/dark_logo.svg', DarkLogoPath: '/dark_logo.svg',
api_prefix: 'ycorejs_apiv3', api_prefix: 'ycorejs_apiv3',
app_settings_storage: 'app_settings' app_settings_storage: 'app_settings',
appTheme_container: 'app_theme',
appTheme_desiredContrast: 4.5,
// Contrast level AA = 4.5, Level AAA = 7
// Reference: https://www.w3.org/WAI/WCAG21/quickref/?versions=2.0&showtechniques=143#qr-visual-audio-contrast-contrast
}, },
i18n: { i18n: {

View File

@ -30,4 +30,5 @@ module.exports = [
title: 'Overlay loose focus', title: 'Overlay loose focus',
description: 'Close the overlay when loose focus', description: 'Close the overlay when loose focus',
}, },
] ]

View File

@ -7,7 +7,8 @@ let MenuList = [
path: '/main', path: '/main',
require: 'login', require: 'login',
icon: <Icons.Home />, icon: <Icons.Home />,
mobile: 'true' mobile: true,
desktop: false
}, },
{ {
id: 'explore', id: 'explore',
@ -15,7 +16,8 @@ let MenuList = [
path: '/explore', path: '/explore',
require: 'login', require: 'login',
icon: <Icons.Compass />, icon: <Icons.Compass />,
mobile: 'true' mobile: true,
desktop: true
}, },
{ {
id: 'saves', id: 'saves',
@ -23,7 +25,8 @@ let MenuList = [
path: '/saves', path: '/saves',
require: 'login', require: 'login',
icon: <Icons.Bookmark />, icon: <Icons.Bookmark />,
mobile: 'false' mobile: false,
desktop: true
}, },
{ {
id: 'messages', id: 'messages',
@ -31,7 +34,8 @@ let MenuList = [
path: '/messages', path: '/messages',
require: 'login', require: 'login',
icon: <Icons.MessageSquare />, icon: <Icons.MessageSquare />,
mobile: 'false' mobile: true,
desktop: true
}, },
{ {
id: 'notifications', id: 'notifications',
@ -39,7 +43,8 @@ let MenuList = [
path: '/notifications', path: '/notifications',
require: 'login', require: 'login',
icon: <Icons.Inbox/>, icon: <Icons.Inbox/>,
mobile: 'false' mobile: true,
desktop: true
}, },
] ]

35
globals/theme_settings.js Normal file
View File

@ -0,0 +1,35 @@
import * as Icons from 'components/Icons'
export default [
{
id: 'backgroundImage',
icon: <Icons.Image />,
title: 'Background Image',
description: 'Change the background of the app',
},
{
id: 'backgroundColor',
icon: <Icons.Droplet />,
title: 'Overlay Color',
description: 'Description blah blah',
},
{
id: 'color',
icon: <Icons.FormatPainterOutlined style={{ marginRight: '10px' }} />,
title: 'Colors',
description: 'Texts, Buttons, Sliders ...etc',
},
{
id: 'fontSize',
icon: <Icons.ZoomIn />,
title: 'Sizes',
description: 'Zoom?',
},
{
id: 'darkmode',
icon: <Icons.Moon />,
title: 'Dark Mode',
description: 'Yeaah, no more daying',
}
]

View File

@ -3,7 +3,7 @@
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4", "UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
"title": "Comty™", "title": "Comty™",
"DevBuild": true, "DevBuild": true,
"version": "0.7.24", "version": "0.7.31",
"stage": "dev-pre", "stage": "dev-pre",
"description": "", "description": "",
"author": "RageStudio", "author": "RageStudio",
@ -24,20 +24,22 @@
"*.{js,jsx,less,md,json}": [ "*.{js,jsx,less,md,json}": [
"prettier --write" "prettier --write"
], ],
"*.ts?(x)": [ "*.ts?(x)": [
"prettier --parser=typescript --write" "prettier --parser=typescript --write"
] ]
}, },
"dependencies": { "dependencies": {
"@icons-pack/react-simple-icons": "^3.8.0",
"@lingui/cli": "^2.9.1", "@lingui/cli": "^2.9.1",
"@lingui/loader": "^2.9.1", "@lingui/loader": "^2.9.1",
"@lingui/react": "^2.9.1", "@lingui/react": "^2.9.1",
"@material-ui/core": "^4.9.9", "@material-ui/core": "^4.9.9",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@ragestudio/ycorejs-lib": "^0.1.21", "@ragestudio/ycorejs-lib": "^0.1.21",
"antd": "^4.3.5", "antd": "^4.5.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"babel-core": "^6.26.3", "babel-core": "^6.26.3",
"big-json": "^3.1.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"colorthief": "^2.3.0", "colorthief": "^2.3.0",
"dva-model-extend": "^0.1.2", "dva-model-extend": "^0.1.2",
@ -70,6 +72,7 @@
"react-scripts": "^3.4.1", "react-scripts": "^3.4.1",
"react-transition-group": "^4.4.1", "react-transition-group": "^4.4.1",
"react-virtualized": "^9.21.2", "react-virtualized": "^9.21.2",
"simple-icons": "^3.3.0",
"stack-trace": "0.0.10", "stack-trace": "0.0.10",
"store": "^2.0.12", "store": "^2.0.12",
"styled-components": "^5.1.1", "styled-components": "^5.1.1",

View File

@ -1,10 +1,10 @@
<svg id="e9915989-cca6-4620-8282-9db0f616b757" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 120"> <svg id="e9915989-cca6-4620-8282-9db0f616b757" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 120">
<path <path
d="M77.55,29.69,92,18.78a1.42,1.42,0,0,0,.25-2,39.2,39.2,0,0,0-56.31-4.21A38.05,38.05,0,0,0,23.23,42a38.09,38.09,0,0,0,3.62,15.1A38.65,38.65,0,0,0,37.8,70.84,39.46,39.46,0,0,0,83.37,73a38.26,38.26,0,0,0,8.41-7.4,1.41,1.41,0,0,0-.23-2L77.65,53a1.43,1.43,0,0,0-1.9.15A17,17,0,0,1,72.55,56a17.75,17.75,0,0,1-9,2.88c-8.32.31-13.62-5.69-14-6.13a17.68,17.68,0,0,1-4.13-10.13,17.93,17.93,0,0,1,4.56-13A17.46,17.46,0,0,1,71.7,26.34a17.3,17.3,0,0,1,4,3.2A1.41,1.41,0,0,0,77.55,29.69Z" d="M77.55,29.69,92,18.78a1.42,1.42,0,0,0,.25-2,39.2,39.2,0,0,0-56.31-4.21A38.05,38.05,0,0,0,23.23,42a38.09,38.09,0,0,0,3.62,15.1A38.65,38.65,0,0,0,37.8,70.84,39.46,39.46,0,0,0,83.37,73a38.26,38.26,0,0,0,8.41-7.4,1.41,1.41,0,0,0-.23-2L77.65,53a1.43,1.43,0,0,0-1.9.15A17,17,0,0,1,72.55,56a17.75,17.75,0,0,1-9,2.88c-8.32.31-13.62-5.69-14-6.13a17.68,17.68,0,0,1-4.13-10.13,17.93,17.93,0,0,1,4.56-13A17.46,17.46,0,0,1,71.7,26.34a17.3,17.3,0,0,1,4,3.2A1.41,1.41,0,0,0,77.55,29.69Z"
style="fill: #333;" style="fill: rgba(51, 51, 51, 0.80);"
/> />
<path <path
d="M13,63.17a2.77,2.77,0,0,1,3.75,1.43A48.38,48.38,0,0,0,32.07,84.53,48.83,48.83,0,0,0,52.34,93.3,47.37,47.37,0,0,0,92.57,81.8a2.77,2.77,0,0,1,4,.3l6.23,7.4a2.79,2.79,0,0,1-.21,3.83,63.83,63.83,0,0,1-6,5,62.21,62.21,0,0,1-7.44,4.7A60.84,60.84,0,0,1,77,108a62.3,62.3,0,0,1-27,1.51A62.51,62.51,0,0,1,40.18,107,61.5,61.5,0,0,1,20.1,95.69,61.73,61.73,0,0,1,2.41,71a2.79,2.79,0,0,1,1.42-3.55Z" d="M13,63.17a2.77,2.77,0,0,1,3.75,1.43A48.38,48.38,0,0,0,32.07,84.53,48.83,48.83,0,0,0,52.34,93.3,47.37,47.37,0,0,0,92.57,81.8a2.77,2.77,0,0,1,4,.3l6.23,7.4a2.79,2.79,0,0,1-.21,3.83,63.83,63.83,0,0,1-6,5,62.21,62.21,0,0,1-7.44,4.7A60.84,60.84,0,0,1,77,108a62.3,62.3,0,0,1-27,1.51A62.51,62.51,0,0,1,40.18,107,61.5,61.5,0,0,1,20.1,95.69,61.73,61.73,0,0,1,2.41,71a2.79,2.79,0,0,1,1.42-3.55Z"
style="fill: #333;" style="fill: rgba(51, 51, 51, 0.80);"
/> />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -5,4 +5,4 @@ export const Patreon = () => (<svg xmlns="http://www.w3.org/2000/svg" width="1em
<polygon points=".012 95.988 17.59 95.988 17.59 .1 .012 .1"/> <polygon points=".012 95.988 17.59 95.988 17.59 .1 .012 .1"/>
</g> </g>
</svg> </svg>
) )

View File

@ -1,4 +1,4 @@
export * from 'feather-reactjs' export * from 'feather-reactjs'
export * from '@ant-design/icons' export * from '@ant-design/icons'
export * from './custom' export * from './custom'
export * from '@icons-pack/react-simple-icons'

View File

@ -16,51 +16,45 @@ const animationStyles = {
}, },
} }
export function SetControls(e) { export const control = {
window.ControlComponent.DummySetControls(e) set: (e) => {
return if (!window.ControlComponent.state.active) {
} window.ControlComponent.setState({ fadein: true })
export function CloseControls() { }
window.ControlComponent.DummyCloseControls() window.ControlComponent.setState({ active: true, render: e })
return },
close: () => {
window.ControlComponent.setState({ fadein: false })
setTimeout(() => window.ControlComponent.setState({ active: false, render: null }), 1000)
}
} }
class Control extends React.PureComponent { export default class Control extends React.PureComponent {
constructor(props) { constructor(props) {
super(props) super(props);
window.ControlComponent = this
this.state = { this.state = {
Show: false, active: false,
FadeIN: true, fadein: true,
} };
} window.ControlComponent = this;
set = e => {
if (this.state.Show == false) {
this.setState({ FadeIN: true })
}
this.setState({ Show: true, RenderFragment: e })
}
close() {
this.setState({ FadeIN: false })
setTimeout(() => this.setState({ Show: false, RenderFragment: null }), 1000)
} }
render() { render() {
const { RenderFragment, Show, FadeIN } = this.state const { render, active, fadein } = this.state
const isMobile = this.props.mobile? this.props.mobile : false const isMobile = this.props.mobile? this.props.mobile : false
return Show ? ( return active ? (
<StyleRoot> <StyleRoot>
<div <div
style={ style={
FadeIN ? animationStyles.fadeInUp : animationStyles.bounceOutDown fadein ? animationStyles.fadeInUp : animationStyles.bounceOutDown
} }
> >
<antd.Card bordered={false} className={classnames(styles.ControlCard, {[styles.mobile]: isMobile})} > <antd.Card bordered={false} className={classnames(styles.ControlCard, {[styles.mobile]: isMobile})} >
<React.Fragment>{RenderFragment}</React.Fragment> <React.Fragment>{render}</React.Fragment>
</antd.Card> </antd.Card>
</div> </div>
</StyleRoot> </StyleRoot>
) : null ) : null
} }
} }
export default Control

View File

@ -10,7 +10,7 @@
background-color: transparent; background-color: transparent;
border: 0!important; border: 0!important;
outline: 0!important; outline: 0!important;
color: #273346; color: @AppTheme_global_color_accent;
padding: 0 0 0 48px; padding: 0 0 0 48px;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 56.966 56.966' fill='%23c1c7cd'%3e%3cpath d='M55.146 51.887L41.588 37.786A22.926 22.926 0 0046.984 23c0-12.682-10.318-23-23-23s-23 10.318-23 23 10.318 23 23 23c4.761 0 9.298-1.436 13.177-4.162l13.661 14.208c.571.593 1.339.92 2.162.92.779 0 1.518-.297 2.079-.837a3.004 3.004 0 00.083-4.242zM23.984 6c9.374 0 17 7.626 17 17s-7.626 17-17 17-17-7.626-17-17 7.626-17 17-17z'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 56.966 56.966' fill='%23c1c7cd'%3e%3cpath d='M55.146 51.887L41.588 37.786A22.926 22.926 0 0046.984 23c0-12.682-10.318-23-23-23s-23 10.318-23 23 10.318 23 23 23c4.761 0 9.298-1.436 13.177-4.162l13.661 14.208c.571.593 1.339.92 2.162.92.779 0 1.518-.297 2.079-.837a3.004 3.004 0 00.083-4.242zM23.984 6c9.374 0 17 7.626 17 17s-7.626 17-17 17-17-7.626-17-17 7.626-17 17-17z'/%3e%3c/svg%3e");
background-repeat: no-repeat; background-repeat: no-repeat;
@ -20,7 +20,7 @@
font-weight: 600; font-weight: 600;
font-size: 15px; font-size: 15px;
&::placeholder { &::placeholder {
color: #a2a2a2; color: @AppTheme_global_color_accent;
} }
transition: all 150ms ease-in-out; transition: all 150ms ease-in-out;

View File

@ -117,7 +117,7 @@ export default class Overlay extends React.PureComponent {
} }
return( return(
<Primary id="main" fragment={<> <div><__searchBar /></div> <div><Card_Component> yeah </Card_Component></div> </>} /> <Primary id="main" fragment={<> <div><__searchBar /></div> <div></div> </>} />
) )
} }

View File

@ -15,12 +15,12 @@ export default class Sider_Default extends React.PureComponent {
renderMenus(data){ renderMenus(data){
return data.map(e => { return data.map(e => {
return( return e.desktop? (
<antd.Menu.Item key={e.id}> <antd.Menu.Item key={e.id}>
{e.icon} {e.icon}
<span>{e.title}</span> <span>{e.title}</span>
</antd.Menu.Item> </antd.Menu.Item>
) ) : null
}) })
} }
@ -35,7 +35,7 @@ export default class Sider_Default extends React.PureComponent {
> >
<div className={styles.left_sider_brandholder}> <div className={styles.left_sider_brandholder}>
<img <img
onClick={() => handleClickMenu({key: '/'})} onClick={() => handleClickMenu({key: ''})}
src={logo} src={logo}
/> />
</div> </div>

View File

@ -3,9 +3,10 @@
.left_sider_wrapper { .left_sider_wrapper {
border-color: transparent; border-color: transparent;
font-size: 13px; font-size: 15px;
font-family: @__Global_general_font_family; font-family: @__Global_texted_font;
font-weight: 600;
width: 100%; width: 100%;
height: 100vh; height: 100vh;
@ -15,19 +16,13 @@
background-color: transparent; background-color: transparent;
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
&.matchColor{
h1,h2,h3,h4,h5,span,p,svg {
color: unset;
filter: invert(1);
}
}
:global { :global {
.ant-layout-sider { .ant-layout-sider {
background-color: transparent; background-color: transparent;
float: right; float: right;
.ant-menu { .ant-menu {
font-weight: 700;
color: @AppTheme_global_color;
vertical-align: middle; vertical-align: middle;
// margin: 0 0 0 5px; // margin: 0 0 0 5px;
} }
@ -45,9 +40,10 @@
border-radius: 8px 8px 8px 8px; border-radius: 8px 8px 8px 8px;
transform: translate(10px,0); transform: translate(10px,0);
// background: linear-gradient(49deg, rgba(255,255,255,1) 32%, rgba(255, 255, 255, 0) 100%); // background: linear-gradient(49deg, rgba(255,255,255,1) 32%, rgba(255, 255, 255, 0) 100%);
backdrop-filter: blur(2px); backdrop-filter: blur(2px);
border-left: 10px #FFCC00 solid; // border-left: 10px @app_accent_gradient solid;
box-shadow: -2px 2px 1px 0 rgba(51, 51, 51, 0.13);
color: rgb(102, 102, 102); color: rgb(102, 102, 102);
} }
@ -76,7 +72,7 @@
cursor: pointer; cursor: pointer;
img{ img{
margin: 15px 0 0 0; margin: 15px 0 0 22px;
max-height: 70px; max-height: 70px;
height: 5vh; height: 5vh;
width: 5vh; width: 5vh;
@ -137,7 +133,7 @@
} }
.ant-menu-dark .ant-menu-item a { .ant-menu-dark .ant-menu-item a {
color: rgb(197, 197, 197); color: @AppTheme_global_color;
} }
} }
} }

View File

@ -9,7 +9,7 @@ import Sider_Default from './default'
class Sider extends React.PureComponent { class Sider extends React.PureComponent {
handleClickMenu = e => { handleClickMenu = e => {
router.go(e.key) router.go(`/${e.key}`)
} }
render() { render() {

View File

@ -6,11 +6,10 @@ import styles from './index.less'
export default class Sider_Mobile extends React.PureComponent { export default class Sider_Mobile extends React.PureComponent {
renderMenus(data){ renderMenus(data){
// filter by item with mobile support
return data.map(e => { return data.map(e => {
return( return e.mobile? (
<antd.Menu.Item key={e.id} style={{ color: '#ffffff', fontSize: '18px' }} >{e.icon}</antd.Menu.Item> <antd.Menu.Item key={e.id} style={{ color: '#ffffff', fontSize: '18px' }} >{e.icon}</antd.Menu.Item>
) ) : null
}) })
} }

View File

@ -272,11 +272,11 @@ export function setLocale(language) {
export function get_value(source,key){ export function get_value(source,key){
if( !key || !source ) return false if( !key || !source ) return false
try { try {
const map = source.map(item => { const find = source.find(item => {
return item.id === key? item.value : null return item.id === key
}) })
const fr = map.filter(Boolean) return find.value
return fr.toString()
} }
catch (error) { catch (error) {
return false return false

View File

@ -1,2 +1,14 @@
import { notify } from 'core/libs/interface/notify'
import verbosity from 'core/libs/verbosity'
// STRINGS // STRINGS
export const OVERLAY_BADPOSITION = `Invalid overlay position! Was expected "primary" or "secondary"` export const OVERLAY_BADPOSITION = `Invalid overlay position! Was expected "primary" or "secondary"`
export const INTERNAL_PROCESS_FAILED = `An internal error has occurred! `
// HANDLERS
export const onError = {
internal_proccess: (...rest) => {
verbosity.error(...rest)
notify.warn(INTERNAL_PROCESS_FAILED, ...rest)
return false
}
}

View File

@ -0,0 +1,161 @@
// JSON.prune : a function to stringify any object without overflow
// two additional optional parameters :
// - the maximal depth (default : 6)
// - the maximal length of arrays (default : 50)
// You can also pass an "options" object.
// examples :
// var json = JSON.prune(window)
// var arr = Array.apply(0,Array(1000)); var json = JSON.prune(arr, 4, 20)
// var json = JSON.prune(window.location, {inheritedProperties:true})
// Web site : http://dystroy.org/JSON.prune/
// JSON.prune on github : https://github.com/Canop/JSON.prune
// This was discussed here : http://stackoverflow.com/q/13861254/263525
// The code is based on Douglas Crockford's code : https://github.com/douglascrockford/JSON-js/blob/master/json2.js
// No effort was done to support old browsers. JSON.prune will fail on IE8.
'use strict';
var DEFAULT_MAX_DEPTH = 6;
var DEFAULT_ARRAY_MAX_LENGTH = 50;
var DEFAULT_PRUNED_VALUE = '"-pruned-"';
var seen; // Same variable used for all stringifications
var iterator; // either forEachEnumerableOwnProperty, forEachEnumerableProperty or forEachProperty
// iterates on enumerable own properties (default behavior)
var forEachEnumerableOwnProperty = function(obj, callback) {
for (var k in obj) {
if (Object.prototype.hasOwnProperty.call(obj, k)) callback(k);
}
};
// iterates on enumerable properties
var forEachEnumerableProperty = function(obj, callback) {
for (var k in obj) callback(k);
};
// iterates on properties, even non enumerable and inherited ones
// This is dangerous
var forEachProperty = function(obj, callback, excluded) {
if (obj==null) return;
excluded = excluded || {};
Object.getOwnPropertyNames(obj).forEach(function(k){
if (!excluded[k]) {
callback(k);
excluded[k] = true;
}
});
forEachProperty(Object.getPrototypeOf(obj), callback, excluded);
};
Object.defineProperty(Date.prototype, "toPrunedJSON", {value:Date.prototype.toJSON});
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
};
function quote(string) {
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string'
? c
: '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
var prune = function (value, depthDecr, arrayMaxLength) {
var prunedString = DEFAULT_PRUNED_VALUE;
var replacer;
if (typeof depthDecr == "object") {
var options = depthDecr;
depthDecr = options.depthDecr;
arrayMaxLength = options.arrayMaxLength;
iterator = options.iterator || forEachEnumerableOwnProperty;
if (options.allProperties) iterator = forEachProperty;
else if (options.inheritedProperties) iterator = forEachEnumerableProperty
if ("prunedString" in options) {
prunedString = options.prunedString;
}
if (options.replacer) {
replacer = options.replacer;
}
} else {
iterator = forEachEnumerableOwnProperty;
}
seen = [];
depthDecr = depthDecr || DEFAULT_MAX_DEPTH;
arrayMaxLength = arrayMaxLength || DEFAULT_ARRAY_MAX_LENGTH;
function str(key, holder, depthDecr) {
var i, k, v, length, partial, value = holder[key];
if (value && typeof value === 'object' && typeof value.toPrunedJSON === 'function') {
value = value.toPrunedJSON(key);
}
if (value && typeof value.toJSON === 'function') {
value = value.toJSON();
}
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
return String(value);
case 'object':
if (!value) {
return 'null';
}
if (depthDecr<=0 || seen.indexOf(value)!==-1) {
if (replacer) {
var replacement = replacer(value, prunedString, true);
return replacement===undefined ? undefined : ''+replacement;
}
return prunedString;
}
seen.push(value);
partial = [];
if (Object.prototype.toString.apply(value) === '[object Array]') {
length = Math.min(value.length, arrayMaxLength);
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value, depthDecr-1) || 'null';
}
v = '[' + partial.join(',') + ']';
if (replacer && value.length>arrayMaxLength) return replacer(value, v, false);
return v;
}
if (value instanceof RegExp) {
return quote(value.toString());
}
iterator(value, function(k) {
try {
v = str(k, value, depthDecr-1);
if (v) partial.push(quote(k) + ':' + v);
} catch (e) {
// this try/catch due to forbidden accessors on some objects
}
});
return '{' + partial.join(',') + '}';
case 'function':
case 'undefined':
return replacer ? replacer(value, undefined, false) : undefined;
}
}
return str('', {'': value}, depthDecr);
};
prune.log = function() {
console.log.apply(console, Array.prototype.map.call(arguments, function(v) {
return JSON.parse(JSON.prune(v));
}));
};
prune.forEachProperty = forEachProperty; // you might want to also assign it to Object.forEachProperty
export default prune

View File

@ -1,7 +1,6 @@
import { defaults, app_config } from 'config' import { defaults, app_config } from 'config'
import { get_value } from 'core' import { get_value } from 'core'
export function parseLocalStorage(){ export function parseLocalStorage(){
const a = localStorage.getItem(app_config.app_settings_storage) const a = localStorage.getItem(app_config.app_settings_storage)
try { try {
@ -10,41 +9,71 @@ export function parseLocalStorage(){
console.log(error) console.log(error)
} }
} }
export function newSetting(key, value) {
export function settingValue(key){ let setting = {}
let tmp = []; setting.id = key
setting.value = value
const keys = Object.keys(defaults) return [setting]
const values = Object.values(defaults)
const length = keys.length
for (let i = 0; i < length; i++) {
const storagedValue = get_value(parseLocalStorage(), keys[i])
tmp[keys[i]] = (storagedValue? storagedValue : values[i])
}
if (key) {
return tmp[key]
}
return tmp
} }
export const storage = { export const settings = {
get: (key) => { get: (key) => {
return settingValue(key) let tmp = [];
const keys = Object.keys(defaults)
const values = Object.values(defaults)
const length = keys.length
for (let i = 0; i < length; i++) {
if(parseLocalStorage()){
const storagedValue = parseLocalStorage().find(item => {
return item.id === keys[i]
})
if (typeof(storagedValue) == 'undefined') {
tmp[keys[i]] = values[i]
}else{
tmp[keys[i]] = storagedValue.value
}
}
else{
tmp[keys[i]] = values[i]
}
}
if (key) {
return tmp[key]
}
return tmp
}, },
set: (key, value) => { set: (key, value) => {
const storaged = parseLocalStorage() let tmp
const updated = storaged.map(element => { let data = parseLocalStorage()
return element.id === key? Object.assign(element, { value: value }) : element
}); if (data) {
const finded = data.find(element => {
return element.id === key
})
if (!finded) {
const parsed = data.concat(newSetting(key, value))
tmp = parsed
} else {
const updated = data.map(element => {
return element.id === key? Object.assign(element, { value: value }) : element
})
tmp = updated
}
}else{
tmp = newSetting(key, value)
}
data = tmp
try { try {
localStorage.setItem( app_config.app_settings_storage, JSON.stringify(updated) ) localStorage.setItem( app_config.app_settings_storage, JSON.stringify(data) )
} catch (error) { } catch (error) {
console.log(error) console.log(error)
return false
} }
} }
} }
export default settingValue export default (e) => settings.get(e)

View File

@ -0,0 +1,237 @@
import store from 'store';
import { app_config } from 'config';
import verbosity from 'core/libs/verbosity'
import * as errorHandlers from 'core/libs/errorhandler'
import { negate } from 'lodash';
const { appTheme_desiredContrast, appTheme_container } = app_config
export const theme = {
get: (key) => {
if(!localStorage.getItem(appTheme_container)) return false
const raw = store.get(appTheme_container)
let container = []
if (raw) {
raw.forEach((e)=>{
container[e.key] = e.value
})
return container
}
return null
},
set: (data) => {
if (!data || data.length > 2) return false
try {
let mix = []
const obj = Object.entries(data)
obj.forEach((e) => {
mix.push({key: e[0], value: e[1]})
})
return store.set(appTheme_container, mix)
} catch (error) {
console.log(error)
return false
}
},
raw: () => {
return store.get(appTheme_container)
}
}
export function get_style_rule_value(selector, style)
{
const selector_lowercase = selector.toLowerCase();
const selector_parsed = selector_lowercase.substr(0,1)==='.' ? selector_lowercase.substr(1) : '.'+selector_lowercase;
for (let i = 0; i < document.styleSheets.length; i++)
{
let styleSheet = document.styleSheets[i];
let rules = styleSheet.cssRules ? styleSheet.cssRules : styleSheet.rules;
for (var j = 0; j < rules.length; j++)
{
if (rules[j].selectorText)
{
var check = rules[j].selectorText.toLowerCase();
switch (check)
{
case selector_lowercase :
case selector_parsed : return rules[j].style[style];
}
}
}
}
}
export function getOptimalOpacityFromIMG(payload, callback) {
const { textColor, overlayColor, img } = payload;
verbosity.debug(payload)
let canvas = document.createElement('canvas');
let image = new Image();
image.src = img
image.setAttribute('crossOrigin', '');
image.onload = () =>{
const imagePixelColors = getImagePixelColorsUsingCanvas(canvas, image);
if(imagePixelColors){
const worstContrastColorInImage = getWorstContrastColorInImage(textColor, imagePixelColors);
const optimalOpacity = findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast);
return callback(optimalOpacity)
}else{
return false
}
}
}
export function getImagePixelColorsUsingCanvas(canvas, image) {
let imagePixelColors = null;
const ctx = canvas.getContext('2d');
canvas.height = getCanvasHeightToMatchImageProportions(canvas, image);
const sourceImageCoordinates = [0, 0, image.width, image.height];
const destinationCanvasCoordinates = [0, 0, canvas.width, canvas.height];
ctx.drawImage(
image,
...sourceImageCoordinates,
...destinationCanvasCoordinates
);
// Remember getImageData only works for same-origin or cross-origin-enabled images.
// See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image for more info.
try {
imagePixelColors = ctx.getImageData(...destinationCanvasCoordinates);
} catch (error) {
return errorHandlers.onError.internal_proccess(error)
}
if (imagePixelColors) {
return imagePixelColors
}
}
export function getCanvasHeightToMatchImageProportions(canvas, image) {
return (image.height / image.width) * canvas.width;
}
export function getWorstContrastColorInImage(textColor, imagePixelColors) {
let worstContrastColorInImage;
let worstContrast = Infinity;
for (let i = 0; i < imagePixelColors.data.length; i += 4) {
let pixelColor = {
r: imagePixelColors.data[i],
g: imagePixelColors.data[i + 1],
b: imagePixelColors.data[i + 2],
};
let contrast = getContrast(textColor, pixelColor);
if(contrast < worstContrast) {
worstContrast = contrast;
worstContrastColorInImage = pixelColor;
}
}
return worstContrastColorInImage;
}
export function getContrast(color1, color2) {
const color1_luminance = getLuminance(color1);
const color2_luminance = getLuminance(color2);
const lighterColorLuminance = Math.max(color1_luminance, color2_luminance);
const darkerColorLuminance = Math.min(color1_luminance, color2_luminance);
const contrast = (lighterColorLuminance + 0.05) / (darkerColorLuminance + 0.05);
return contrast;
}
export function getLuminance({r,g,b}) {
return (0.2126 * getLinearRGB(r) + 0.7152 * getLinearRGB(g) + 0.0722 * getLinearRGB(b));
}
export function getLinearRGB(primaryColor_8bit) {
// First convert from 8-bit rbg (0-255) to standard RGB (0-1)
const primaryColor_sRGB = convert_8bit_RGB_to_standard_RGB(primaryColor_8bit);
// Then convert from sRGB to linear RGB so we can use it to calculate luminance
const primaryColor_RGB_linear = convert_standard_RGB_to_linear_RGB(primaryColor_sRGB);
return primaryColor_RGB_linear;
}
export function convert_8bit_RGB_to_standard_RGB(primaryColor_8bit) {
return primaryColor_8bit / 255;
}
export function convert_standard_RGB_to_linear_RGB(primaryColor_sRGB) {
const primaryColor_linear = primaryColor_sRGB < 0.03928 ?
primaryColor_sRGB/12.92 :
Math.pow((primaryColor_sRGB + 0.055) / 1.055, 2.4);
return primaryColor_linear;
}
export function getTextContrastWithImagePlusOverlay({textColor, overlayColor, imagePixelColor, overlayOpacity}) {
const colorOfImagePixelPlusOverlay = mixColors(imagePixelColor, overlayColor, overlayOpacity);
const contrast = getContrast(textColor, colorOfImagePixelPlusOverlay);
return contrast;
}
export function mixColors(baseColor, overlayColor, overlayOpacity) {
const mixedColor = {
r: baseColor.r + (overlayColor.r - baseColor.r) * overlayOpacity,
g: baseColor.g + (overlayColor.g - baseColor.g) * overlayOpacity,
b: baseColor.b + (overlayColor.b - baseColor.b) * overlayOpacity,
}
return mixedColor;
}
export function findOptimalOverlayOpacity(textColor, overlayColor, worstContrastColorInImage, appTheme_desiredContrast) {
const opacityGuessRange = {
lowerBound: 0,
midpoint: 0.5,
upperBound: 1,
};
let numberOfGuesses = 0;
const maxGuesses = 8;
const opacityLimit = 0.99;
while (numberOfGuesses < maxGuesses) {
numberOfGuesses++;
const currentGuess = opacityGuessRange.midpoint;
const contrastOfGuess = getTextContrastWithImagePlusOverlay({
textColor,
overlayColor,
imagePixelColor: worstContrastColorInImage,
overlayOpacity: currentGuess,
});
const isGuessTooLow = contrastOfGuess < appTheme_desiredContrast;
const isGuessTooHigh = contrastOfGuess > appTheme_desiredContrast;
if (isGuessTooLow) {
opacityGuessRange.lowerBound = currentGuess;
}
else if (isGuessTooHigh) {
opacityGuessRange.upperBound = currentGuess;
}
const newMidpoint = ((opacityGuessRange.upperBound - opacityGuessRange.lowerBound) / 2) + opacityGuessRange.lowerBound;
opacityGuessRange.midpoint = newMidpoint;
}
const optimalOpacity = opacityGuessRange.midpoint;
if (optimalOpacity > opacityLimit) {
return opacityLimit;
}
return optimalOpacity;
}

View File

@ -1,7 +1,7 @@
import { storage } from 'core/libs/settings' import settings from 'core/libs/settings'
import stackTrace from 'stack-trace' import stackTrace from 'stack-trace'
// import path from 'path' // import path from 'path'
const verbosity_enabled = storage.get('verbosity') const verbosity_enabled = settings('verbosity')
const verbosity = { const verbosity = {
log: (...cont) => { log: (...cont) => {

View File

@ -12,6 +12,7 @@ import store from 'store'
import classnames from 'classnames' import classnames from 'classnames'
import { app_config } from 'config' import { app_config } from 'config'
import { theme } from 'core/libs/style'
import * as antd from 'antd' import * as antd from 'antd'
import styles from './PrimaryLayout.less' import styles from './PrimaryLayout.less'
@ -23,12 +24,13 @@ const { Sider, Control, Overlay } = MyLayout
@connect(({ app, loading }) => ({ app, loading })) @connect(({ app, loading }) => ({ app, loading }))
class PrimaryLayout extends React.PureComponent { class PrimaryLayout extends React.PureComponent {
constructor(props) { constructor(props) {
super(props) super(props);
window.PrimaryComponent = this
this.state = { this.state = {
collapsed: app_config.default_collapse_sider ? true : false, collapsed: app_config.default_collapse_sider ? true : false,
isMobile: false, isMobile: false,
} },
window.PrimaryComponent = this;
} }
componentDidMount() { componentDidMount() {
@ -52,10 +54,25 @@ class PrimaryLayout extends React.PureComponent {
store.set('collapsed', !fromStore) store.set('collapsed', !fromStore)
} }
Swapper = { renderThemeComponents() {
const currentTheme = theme.get()
} if (!currentTheme) return false
if (currentTheme.backgroundImage) {
return currentTheme.backgroundImage.active? <div style={{
backgroundImage: `url(${currentTheme.backgroundImage.src})`,
transition: "all 150ms linear",
position: 'absolute',
width: '100vw',
height: '100vh',
backgroundRepeat: "repeat-x",
backgroundSize: "cover",
backgroundPositionY: "center",
overflow: "hidden",
opacity: currentTheme.backgroundImage.opacity
}} /> : null
}
}
render() { render() {
const { location, dispatch, children } = this.props const { location, dispatch, children } = this.props
const { collapsed, isMobile } = this.state const { collapsed, isMobile } = this.state
@ -77,6 +94,8 @@ class PrimaryLayout extends React.PureComponent {
return ( return (
<React.Fragment> <React.Fragment>
<Control />
{this.renderThemeComponents()}
<antd.Layout id="primaryLayout" className={classnames(styles.primary_layout, {[styles.mobile]: isMobile})}> <antd.Layout id="primaryLayout" className={classnames(styles.primary_layout, {[styles.mobile]: isMobile})}>
<Sider {...SiderProps} /> <Sider {...SiderProps} />
<div className={styles.primary_layout_container}> <div className={styles.primary_layout_container}>

View File

@ -26,7 +26,8 @@ export default {
controlActive: false, controlActive: false,
feedOutdated: false, feedOutdated: false,
app_settings: store.get('app_settings'), app_settings: store.get(app_config.app_settings_storage),
app_theme: store.get(app_config.appTheme_container),
notifications: [], notifications: [],
locationQuery: {}, locationQuery: {},
@ -52,7 +53,7 @@ export default {
cancelRequest.forEach((value, key) => { cancelRequest.forEach((value, key) => {
if (value.pathname !== window.location.pathname) { if (value.pathname !== window.location.pathname) {
value.cancel(CANCEL_REQUEST_MESSAGE); value.cancel('cancel request');
cancelRequest.delete(key); cancelRequest.delete(key);
} }
}); });
@ -113,6 +114,25 @@ export default {
} }
}); });
}, },
*updateTheme({payload}, {call, put, select}){
if (!payload) return false;
let tmp = []
const keys = Object.keys(payload)
const values = Object.values(payload)
const lenght = keys.length
for (let i = 0; i < lenght; i++) {
let obj = {}
obj.key = keys[i]
obj.value = values[i]
tmp[i] = obj
}
return yield put({ type: 'handleUpdateTheme', payload: tmp });
},
}, },
reducers: { reducers: {
updateState(state, { payload }) { updateState(state, { payload }) {
@ -131,6 +151,12 @@ export default {
state.notifications = []; state.notifications = [];
}, },
handleUpdateTheme(state, { payload }) {
verbosity.debug(payload)
store.set(app_config.appTheme_container, payload);
state.app_theme = payload
},
disconnectServices(state) { disconnectServices(state) {
state.service_valid = false; state.service_valid = false;
state.session_valid = false; state.session_valid = false;

View File

@ -14,7 +14,6 @@ import {
import classnames from 'classnames'; import classnames from 'classnames';
import { CloseOutlined } from '@ant-design/icons'; import { CloseOutlined } from '@ant-design/icons';
import endpoints_list from 'config/endpoints'; import endpoints_list from 'config/endpoints';
import { Page } from 'components';
import styles from './index.less'; import styles from './index.less';
@ -171,7 +170,7 @@ class RequestPage extends React.Component {
const { result, url, method, ParamsKeys, BodyKeys, visible } = this.state; const { result, url, method, ParamsKeys, BodyKeys, visible } = this.state;
return ( return (
<Page inner> <div>
<Row> <Row>
<Col lg={8} md={24}> <Col lg={8} md={24}>
<List <List
@ -343,7 +342,7 @@ class RequestPage extends React.Component {
<div className={styles.result}>{result}</div> <div className={styles.result}>{result}</div>
</Col> </Col>
</Row> </Row>
</Page> </div>
); );
} }
} }

79
src/pages/debug/theme.js Normal file
View File

@ -0,0 +1,79 @@
import React from 'react'
import * as antd from 'antd'
import * as themeLIB from 'core/libs/style'
function getBase64(img, callback) {
const reader = new FileReader()
reader.addEventListener('load', () => callback(reader.result))
reader.readAsDataURL(img)
}
export default class themedebug extends React.PureComponent {
state = {
textColor: {r: '255', g: '255', b: '255'},
overlayColor: {r: '0', g: '0', b: '0'},
optimal: null,
file: null,
fileURL: null,
}
ToogleUpload() {
this.setState({ uploader: !this.state.uploader })
}
handleDeleteFile = () => {
this.setState({ fileURL: null })
}
handleFileUpload = info => {
if (info.file.status === 'uploading') {
this.setState({ loading: true })
return
}
if (info.file.status === 'done') {
this.setState({ file: info.file.originFileObj, uploader: false })
getBase64(info.file.originFileObj, fileURL => {
this.setState({ fileURL, loading: false })
})
}
}
handleGetOptimal() {
const optimal = themeLIB.getOptimalOpacityFromIMG({ textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: this.state.fileURL })
this.setState({ optimal: optimal })
}
schemeToRGB(values) {
return `rgb(${values.r}, ${values.g}, ${values.b})`
}
render(){
return(
<div style={{ wordBreak: 'keep-all' }}>
<antd.Button onClick={() => this.handleGetOptimal()}> Get OPACITY </antd.Button>
<antd.Upload
multiple="false"
onChange={this.handleFileUpload}
>
<antd.Button>
Click to Upload
</antd.Button>
</antd.Upload>
<div>
{JSON.stringify(this.state.file)}<br/><br/>
textColor:{JSON.stringify(this.state.textColor)}<br/><br/>
overlayColor:{JSON.stringify(this.state.overlayColor)}<br/><br/>
</div>
<div style={{ position: 'absolute', backgroundColor: this.schemeToRGB(this.state.overlayColor) , display: 'flex', width: '500px', height: '500px' }}>
<h2 style={{ position: 'absolute', zIndex: '10', color: this.schemeToRGB(this.state.textColor) }}>Sample text</h2>
<img style={{ position: 'absolute',opacity: this.state.optimal, zIndex: '9', width: '500px' }} src={this.state.fileURL} />
</div>
</div>
)
}
}

View File

@ -1,30 +1,29 @@
import React, { Component, Fragment } from 'react' import React, { Component, Fragment } from 'react'
import { List, Switch, Button, notification, InputNumber } from 'antd' import { List, Switch, Button, notification, InputNumber } from 'antd'
import ListSettings from 'globals/settings' import ListSettings from 'globals/settings'
import ControlBar from 'components/layout/ControlBar' import { control } from 'components/layout/ControlBar'
import verbosity from 'core/libs/verbosity' import verbosity from 'core/libs/verbosity'
import * as Icons from 'components/Icons' import * as Icons from 'components/Icons'
import { settings, newSetting } from 'core/libs/settings'
class Base extends Component { class Base extends Component {
constructor(props) { constructor(props) {
super(props), super(props);
(this.state = { this.state = {
SettingRepo: ListSettings, list: ListSettings,
forSave: false, };
})
} }
rendersets = item => { renderSetting = item => {
let e = item.type switch (item.type) {
switch (e) {
case 'switch': case 'switch':
return ( return (
<Switch <Switch
checkedChildren={'Enabled'} checkedChildren={'Enabled'}
unCheckedChildren={'Disabled'} unCheckedChildren={'Disabled'}
checked={item.value ? true : false} checked={settings.get(item.id)}
onChange={() => this.onChangeSwitch(item)} onChange={() => this.onChange(item)}
/> />
) )
case 'numeric': case 'numeric':
@ -41,29 +40,6 @@ class Base extends Component {
} }
} }
SettingRender = data => {
try {
return (
<div>
<List
itemLayout="horizontal"
dataSource={data}
renderItem={item => (
<List.Item actions={item.actions} key={item.SettingID}>
<List.Item.Meta
title={item.title}
description={item.description}
/>
{this.rendersets(item)}
</List.Item>
)}
/>
</div>
)
} catch (err) {
return verbosity.log(err)
}
}
handleControlBar() { handleControlBar() {
const ListControls = [ const ListControls = [
<div key={Math.random()}> <div key={Math.random()}>
@ -76,7 +52,7 @@ class Base extends Component {
</Button> </Button>
</div>, </div>,
] ]
ControlBar.set(ListControls) control.set(ListControls)
} }
saveChanges() { saveChanges() {
@ -87,39 +63,44 @@ class Base extends Component {
description: description:
'The configuration has been saved, it may for some configuration to make changes you need to reload the application', 'The configuration has been saved, it may for some configuration to make changes you need to reload the application',
}) })
setTimeout(app._app.refresh(), 1000) control.close()
ControlBar.close()
} }
onChangeSwitch(item) { onChange(item) {
try { try {
this.handleControlBar() switch (item.type) {
const to = !item.value case 'switch': {
const updatedValue = [...this.state.SettingRepo].map(ita => item.to = !settings.get(item.id)
ita === item ? Object.assign(ita, { value: to }) : ita verbosity.debug(`Changing setting (${item.id}: ${settings.get(item.id)}) => ${item.to}`)
) settings.set(item.id, item.to)
this.setState({ SettingRepo: updatedValue, forSave: true }) this.handleChange(item)
verbosity.log(`Changing ${item.SettingID} to value ${to}`)
}
case 'numeric': {
}
default: {
return null
}
}
} catch (err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
onChangeNumeric(value, item) {
this.HandleChangeNumeric(value)
} handleChange(item) {
HandleChangeNumeric(item, value) {
try { try {
this.handleControlBar() const updatedValue = this.state.list.map(element =>
console.log(item.SettingID, value) element.id === item.id ? Object.assign(element, { value: item.to }) : element
const updatedValue = [...this.state.SettingRepo].map(ita =>
ita === item ? Object.assign(ita, { value: value }) : ita
) )
this.setState({ SettingRepo: updatedValue, forSave: true }) this.setState({ list: updatedValue})
verbosity.log(`Changing ${item.SettingID} to value ${to}`)
} catch (err) { } catch (err) {
console.log(err) console.log(err)
} }
} }
render() { render() {
return ( return (
<Fragment> <Fragment>
@ -127,7 +108,19 @@ class Base extends Component {
<h1> <h1>
<Icons.PullRequestOutlined /> Behaviors <Icons.PullRequestOutlined /> Behaviors
</h1> </h1>
{this.SettingRender(this.state.SettingRepo)} <List
itemLayout="horizontal"
dataSource={this.state.list}
renderItem={item => (
<List.Item actions={item.actions} key={item.id}>
<List.Item.Meta
title={item.title}
description={item.description}
/>
{this.renderSetting(item)}
</List.Item>
)}
/>
</div> </div>
</Fragment> </Fragment>
) )

View File

@ -1,12 +1,35 @@
import React from 'react' import React from 'react'
import * as Feather from 'feather-reactjs' import * as Icons from 'components/Icons'
import * as antd from 'antd' import * as antd from 'antd'
import themeSettings from 'globals/theme_settings'
import {connect} from 'umi'
import styles from './index.less'
import json_prune from 'core/libs/json_prune'
import { SketchPicker } from 'react-color'; import { SketchPicker } from 'react-color';
import { theme, getOptimalOpacityFromIMG, get_style_rule_value } from 'core/libs/style'
import ColorThief from 'colorthief/dist/color-thief'
var colorThief = new ColorThief();
function getBase64(img, callback) {
const reader = new FileReader()
reader.addEventListener('load', () => callback(reader.result))
reader.readAsDataURL(img)
}
function toDataUrl(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
callback(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
class BackgroundColor extends React.Component{ class BackgroundColor extends React.Component{
state = { state = {
@ -29,30 +52,219 @@ class BackgroundColor extends React.Component{
} }
} }
@connect(({ app }) => ({ app }))
class BackgroundImage extends React.Component{ class BackgroundImage extends React.Component{
state = { state = {
results: [] customURL: '',
fileURL: null,
processing: null,
model: { active: false, opacity: null, src: null }
} }
search(key){ handleFileUpload = info => {
if (!key) return false if (info.file.status === 'uploading') {
app.api_unsplash.search(key, (res) =>{ return this.setState({ processing: false })
console.log(res) }
this.setState({ results: res }) if (info.file.status === 'done') {
this.setState({ processing: true })
getBase64(info.file.originFileObj, fileURL => {
this.setState({ fileURL: fileURL })
this.proccessBackground(fileURL)
})
}
}
handleCustomURL(url){
this.setState({ processing: true })
this.setState({ fileURL: url })
toDataUrl(url, fileURL => {
this.proccessBackground(fileURL)
}) })
} }
returnString(url){
this.props.selectImg(`url(${url})`) handleUpdate(payload){
if (!payload) {
payload = this.state.model
}
this.setState({ model: payload, processing: false })
this.props.dispatch({
type: 'app/updateTheme',
payload: { backgroundImage: payload }
});
} }
handleErase(){
this.handleUpdate({})
}
handleExport(){
const string = JSON.stringify(this.state.model)
const exportCodeRender = () => {
if(string.length > 500){
return <div style={{ textAlign: 'center', width: '100%', padding: '30px 0 30px 0' }}>
<Icons.HardDrive style={{ fontSize: '45px', margin: '0' }} />
<h4>Hey, this file is too much large!</h4>
<span>So it couldn't be displayed.</span>
</div>
}
return <div>
{string}
</div>
}
antd.Modal.confirm({
title: <div><Icons.Code /> Your export <antd.Tag> JSON </antd.Tag></div>,
icon: null,
onOk: () => {
let tmp = document.createElement('a')
tmp.href = `data:text/json;charset=utf-8,${encodeURIComponent(string)}`
tmp.download="export.json"
tmp.click()
},
okText: <><Icons.Download />Download as File</> ,
cancelText: "Done",
content: exportCodeRender(),
});
}
proccessBackground(data){
getOptimalOpacityFromIMG({textColor: this.state.textColor, overlayColor: this.state.overlayColor, img: data}, (res) => {
this.handleUpdate({active: true, src: this.state.fileURL, opacity: res})
})
}
schemeToRGB(values){
const scheme = values || { r: '0', g: '0', b: '0' }
const r = scheme.r || '0'
const g = scheme.g || '0'
const b = scheme.b || '0'
return `rgb(${r}, ${g}, ${b})`
}
rgbToScheme(rgb){
const values = rgb.replace(/[^\d,]/g, '').split(',');
return {r: values[0], g: values[1], b: values[2]}
}
componentDidMount(){
const storaged = theme.get()["backgroundImage"]
if(storaged){
this.setState({ model: storaged })
}
let textColor = this.rgbToScheme(get_style_rule_value('#root', 'color'))
let overlayColor = this.rgbToScheme(get_style_rule_value('#root', 'backgroundColor'))
this.setState({
textColor: textColor,
overlayColor: overlayColor
})
}
render(){ render(){
const promiseState = async state => new Promise(resolve => this.setState(state, resolve));
const PreviewModel = () => {
return(
<div>
<h3><Icons.Layout /> Preview</h3>
{ this.state.model.src? <div className={styles.background_image_preview} style={{ backgroundColor: this.schemeToRGB(this.state.overlayColor) }}>
<div style={{ color: `${this.schemeToRGB(this.state.textColor)}!important` }} className={styles.text_wrapper}>
<h1 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h1>
<h2 style={{ color: this.schemeToRGB(this.state.textColor) }}>Sample text</h2>
<h3 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h3>
<h4 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h4>
<p style={{ color: this.schemeToRGB(this.state.textColor) }}>Some text here</p>
<p style={{ color: this.schemeToRGB(this.state.accentColor) }}>Some text here</p>
</div>
<img style={{ opacity: this.state.model.opacity }} src={this.state.model.src} />
</div> : <h3 style={{ textAlign: 'center' }} > No Background </h3> }
</div>
)
}
return ( return (
<> <>
<h3> <Feather.Image /> Upload </h3> <div>
<h2><Icons.Image/> Background Image</h2>
</div>
<antd.Divider type="horizontal" dashed />
<div className={styles.background_image_controls} >
<div>
<h4><Icons.Eye />Enabled</h4>
<antd.Switch
checkedChildren="Enabled"
unCheckedChildren="Disabled"
loading={this.state.processing}
onChange={(e) => {
promiseState(prevState => ({ model: { ...prevState.model, active: e }})).then(() => this.handleUpdate())
}}
checked={this.state.model.active}
/>
</div>
<div>
<h4><Icons.Layers />Opacity</h4>
<antd.Slider disabled={!this.state.model.src} onChange={(e) => {
this.setState(
prevState => ({
model: {
...prevState.model,
opacity: e/100
}
})
)
}} onAfterChange={() => this.handleUpdate()} value={this.state.model.opacity*100} />
</div>
<div>
<h4><Icons.Code />Export Code</h4>
<antd.Button disabled={!this.state.model.src} size="small" onClick={() => this.handleExport()}> Export </antd.Button>
</div>
<div>
<h4><Icons.Trash />Erase</h4>
<antd.Popconfirm disabled={!this.state.model.src} placement="topLeft" title="Are you sure?" onConfirm={() => this.handleErase()} okText="Yes" cancelText="No">
<antd.Button disabled={!this.state.model.src} size="small" type="primary" danger > Delete </antd.Button>
</antd.Popconfirm>
</div>
</div>
<antd.Divider type="horizontal" dashed />
<PreviewModel />
<antd.Divider type="horizontal" dashed /> <antd.Divider type="horizontal" dashed />
<h3> Unsplash </h3>
<div>
<h3><Icons.Upload /> Upload </h3>
<div className={styles.background_image_uploader} >
<div>
Upload from your files <br/>
<antd.Upload onChange={this.handleFileUpload}>
<antd.Button icon={<Icons.Upload type="primary" style={{ margin: '5px 0 0 0' }} />} />
</antd.Upload>
</div>
<div>
<h3>Or</h3>
</div>
<div>
Upload from URL
<antd.Input onPressEnter={() => this.handleCustomURL(this.state.customURL)} onChange={e => this.setState({ customURL: e.target.value })} value={this.state.customURL} placeholder="http://example.com/my_coolest_image.jpg" />
</div>
</div>
{this.state.processing? <h4><Icons.LoadingOutlined spin /> Processing image ... </h4> : null}
{this.state.params? JSON.stringify(this.state.params) : null}
</div>
<antd.Divider type="horizontal" dashed />
{/* <h3><Icons.Unsplash style={{ marginRight: "10px", verticalAlign: "-0.125em", width: "1em", height: "1em" }} /> Unsplash </h3>
<antd.Input.Search onSearch={value => this.search(value)} /> <antd.Input.Search onSearch={value => this.search(value)} />
<antd.List itemLayout="vertical" dataSource={this.state.results} renderItem={item => ( <antd.List.Item> <img onClick={() => this.returnString(item.urls.full)} src={item.urls.small} /> </antd.List.Item>) }/> <antd.List itemLayout="vertical" dataSource={this.state.results} renderItem={item => ( <antd.List.Item> <img onClick={() => this.returnString(item.urls.full)} src={item.urls.small} /> </antd.List.Item>) }/> */}
</> </>
) )
} }
@ -62,96 +274,9 @@ export default class ThemeSettings extends React.PureComponent{
state = { state = {
helper_visible: false, helper_visible: false,
helper_fragment: null, helper_fragment: null,
style: [], style: theme.get(),
} }
componentDidMount(){
this.decodeData()
}
decodeData(){
const storaged = app.app_theme.getString()
try {
if (storaged) {
this.decode(storaged, (res) => {
this.setState({ style: res })
})
}
} catch (error) {
console.log(error)
}
}
encode(data, callback){
if (!data) return false
try {
let mix = []
const obj = Object.entries(data)
obj.forEach((e) => {
mix.push({key: e[0], value: e[1]})
})
return callback(JSON.stringify(mix))
} catch (error) {
console.log(error)
return false
}
}
decode(data, callback){
if (!data) return false
try {
const scheme = JSON.parse(data)
let mix = {};
scheme.forEach((e)=>{
mix[e.key] = e.value
})
return callback(mix)
} catch (error) {
console.log(error)
return false
}
}
handleChanges(key, value){
let { style } = this.state
try {
switch (key) {
case "backgroundImage":{
if(style.backgroundColor){
this.handleRemove("backgroudColor")
}
style[key] = value
this.encode(style, (res) => {
app.app_theme.set(res)
this.setPredominantColor(value)
})
return true
}
case "backgroundColor":{
if(style.backgroundImage){
this.handleRemove("backgroundImage")
}
style[key] = value
this.encode(style, (res) => {
app.app_theme.set(res)
this.handleChanges("predominantColor", value)
})
}
default:{
style[key] = value
this.encode(style, (res) => {
app.app_theme.set(res)
})
}
}
this.decodeData()
} catch (error) {
console.log(error)
}
}
resetStyles(){
app.app_theme.set([])
}
handleRemove(key){ handleRemove(key){
try { try {
@ -172,21 +297,6 @@ export default class ThemeSettings extends React.PureComponent{
} }
setPredominantColor(furl){
const _this = this
const img = new Image();
const url = ((furl.replace("url", "")).substring(1)).slice(0, -1);
img.crossOrigin = 'Anonymous';
img.src = url
img.addEventListener('load', function() {
const color = `rgb(${colorThief.getColor(img)})`
_this.handleChanges("predominantColor", color)
})
}
helper = { helper = {
open: (e) => { open: (e) => {
this.setState({ helper_visible: true, helper_fragment: e }) this.setState({ helper_visible: true, helper_fragment: e })
@ -195,25 +305,33 @@ export default class ThemeSettings extends React.PureComponent{
this.setState({ helper_visible: false, helper_fragment: null }) this.setState({ helper_visible: false, helper_fragment: null })
}, },
backgroundImage: () => { backgroundImage: () => {
this.helper.open(<BackgroundImage selectImg={(i) => this.handleChanges("backgroundImage", i)} />) this.helper.open(<BackgroundImage />)
}, },
backgroundColor: () => { backgroundColor: () => {
this.helper.open(<BackgroundColor changeColor={(i) => this.handleChanges("backgroundColor", i)} />) this.helper.open(<BackgroundColor />)
} }
} }
render(){ render(){
const settingClick = { backgroundImage: () => this.helper.backgroundImage(), backgroundColor: () => this.helper.backgroundColor() }
return( return(
<div> <div>
<h2><Feather.Layers/> Theme</h2> <h2><Icons.Layers/> Theme</h2>
<div> <antd.List
<button onClick={() => this.helper.backgroundImage()}> Background Image</button> itemLayout="horizontal"
<button onClick={() => this.helper.backgroundColor()}> Background Color </button> dataSource={themeSettings}
<button onClick={() => this.resetStyles()}> Reset Style </button> renderItem={item => (
</div> <antd.Card size="small" bodyStyle={{ width: '100%' }} style={{ display: "flex", flexDirection: "row", margin: 'auto' }} hoverable onClick={settingClick[item.id]}>
<h3>{item.icon}{item.title} <div style={{ float: "right" }}><antd.Tag color={this.state.style[item.id]? "green" : "default"} > {this.state.style[item.id]? "Enabled" : "Disabled"} </antd.Tag></div></h3>
<p>{item.description}</p>
</antd.Card>
)}
/>
<antd.Drawer <antd.Drawer
title="Theme Settings"
placement="right" placement="right"
width="500" width="600px"
closable={true} closable={true}
onClose={this.helper.close} onClose={this.helper.close}
visible={this.state.helper_visible} visible={this.state.helper_visible}

View File

@ -0,0 +1,50 @@
.background_image_controls{
width: 100%;
margin: auto;
display: flex;
flex-direction: row;
> div {
width: 100%;
margin: 0 10px;
}
}
.background_image_uploader{
width: 100%;
margin: auto;
display: flex;
flex-direction: row;
text-align: center;
> div {
width: 100%;
margin: 0 10px;
}
}
.background_image_preview{
position: relative;
height: 50%;
img{
z-index: 9;
width: 100%;
border-radius: 8px;
}
.text_wrapper{
position: absolute;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
h1{
font-size: 68px;
}
}
}

View File

@ -11,12 +11,16 @@
@import './components/Menssaging.less'; @import './components/Menssaging.less';
@import './components/PostCard.less'; @import './components/PostCard.less';
@AppTheme_global_color: rgb(51, 51, 51);
@AppTheme_global_color_accent: rgb(162, 162, 162);
@AppTheme_global_background: rgb(248, 246, 248);
@__Global_backgroud_image: ""; @AppTheme_global_color_dark: rgb(162, 162, 162);
@AppTheme_global_background_dark: rgb(51, 51, 51);
@__Global_general_font_family: "Poppins", sans-serif; @__Global_general_font_family: "Poppins", sans-serif;
@__Global_texted_font: "Manrope", sans-serif; @__Global_texted_font: "Manrope", sans-serif;
@__Global_alternative_font: "Netflix Sans"; @__Global_alternative_font: "Netflix Sans", sans-serif;
@__Global_layout_backgroud: #F8F6F8; @__Global_layout_backgroud: #F8F6F8;
@__Global_layout_color: #2d2d2d; @__Global_layout_color: #2d2d2d;
@ -24,22 +28,19 @@
@__Global_layout_transitions-dur: 200ms; @__Global_layout_transitions-dur: 200ms;
@__Global_Components_transitions_dur: 150ms; @__Global_Components_transitions_dur: 150ms;
@__Global_SwapAnimDuration: 170ms; @__Global_SwapAnimDuration: 170ms;
@__Global_backdrop_backgroud: rgba(158, 158, 158, 0.5); @__Global_backdrop_backgroud: rgba(158, 158, 158, 0.5);
@app_accent_gradient: linear-gradient(90deg, rgba(237,111,86,1) 0%, rgba(234,89,89,1) 100%);
@transition-ease-in: all 0.3s ease-out; @transition-ease-in: all 0.3s ease-out;
@transition-ease-out: all 0.3s ease-out; @transition-ease-out: all 0.3s ease-out;
@transition-ease-inout: all 150ms ease-in-out; @transition-ease-inout: all 150ms ease-in-out;
@Overlay_wrapper_hidden_width: 22vw; @Overlay_wrapper_hidden_width: 22vw;
@Overlay_wrapper_showFull_width: 94.2%; @Overlay_wrapper_showFull_width: 94.2%;
@Overlay_wrapper_showHalf_width: 35vw; @Overlay_wrapper_showHalf_width: 35vw;
@Overlay_container_bg_background: #F8F6F8;
@Overlay_container_1_btn_backgroud: #4c4c4c; @Overlay_container_1_btn_backgroud: #4c4c4c;
@ -72,12 +73,14 @@
#root { #root {
overflow: hidden; overflow: hidden;
color: @AppTheme_global_color!important;
background-color: @AppTheme_global_background!important;
--accent_color: @AppTheme_global_color_accent;
} }
body { body {
scroll-behavior: smooth; scroll-behavior: smooth;
height: 100%; height: 100%;
background-color: transparent;
overflow: hidden; overflow: hidden;
@ -86,9 +89,8 @@ body {
font-family: @__Global_texted_font; font-family: @__Global_texted_font;
background-color: @primary_layout_backgroud;
} }
@media (max-width: @bp-small){ @media (max-width: @bp-small){

View File

@ -1,4 +1,4 @@
@left_sider_color: #333; @left_sider_color: @AppTheme_global_color;
@left_sider_sizeIcons: 19px; @left_sider_sizeIcons: 19px;
@left_sider_menu__onhover_backgroud: rgb(80, 80, 80); @left_sider_menu__onhover_backgroud: rgb(80, 80, 80);
@left_sider_menu__onhover_color: rgb(80, 80, 80); @left_sider_menu__onhover_color: rgb(80, 80, 80);