commit 753728e62196aa502d497cfcfbd7318f0a9a98d6 Author: Warinyourself Date: Thu Feb 10 00:34:26 2022 +0300 Hello world diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000..214388f --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,3 @@ +> 1% +last 2 versions +not dead diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..7053c49 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,5 @@ +[*.{js,jsx,ts,tsx,vue}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.env b/.env new file mode 100644 index 0000000..a574e3e --- /dev/null +++ b/.env @@ -0,0 +1 @@ +VUE_APP_VIEW = github \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e69de29 diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..46f9294 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,41 @@ +module.exports = { + root: true, + + env: { + node: true + }, + + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'space-before-function-paren': [2, 'never'], + 'vue/array-bracket-spacing': 'error', + 'vue/arrow-spacing': 'error', + 'vue/block-spacing': 'error', + 'vue/brace-style': 'error', + 'vue/camelcase': 'error', + 'vue/comma-dangle': 'error', + 'vue/component-name-in-template-casing': 'error', + 'vue/eqeqeq': 'error', + 'vue/key-spacing': 'error', + 'vue/match-component-file-name': 'error', + 'vue/object-curly-spacing': 'error', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + }, + + parserOptions: { + parser: '@typescript-eslint/parser', + ecmaVersion: 2020 + }, + + extends: [ + 'eslint:recommended', + 'plugin:vue/recommended', + '@vue/standard', + '@vue/typescript', + 'plugin:vue/essential', + '@vue/typescript/recommended' + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6190a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules +/dist +*.tar.gz + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ad16f7 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# [The Osmos theme](https://warinyourself.github.io/lightdm-webkit2-theme-osmos/) diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000..47c6c1e --- /dev/null +++ b/babel.config.js @@ -0,0 +1,17 @@ +module.exports = { + presets: [ + '@babel/preset-env', + '@vue/cli-plugin-babel/preset' + // 'module:@babel/helper-module-imports' + // [ + // '@vue/app', + // { + // useBuiltIns: 'entry', + // jsx: { + // injectH: false + // } + // } + // ] + ], + plugins: ['transform-vue-jsx', '@babel/plugin-proposal-optional-chaining'] +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..3fe8da8 --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +echo Building LightDM Webkit theme Osmos v$(cat ./version)... +echo + +rm lightdm-webkit-theme-osmos*.tar.gz +rm -r dist/ +yarn build +pushd dist && tar zcvf ../lightdm-webkit-theme-osmos-$(cat ../version).tar.gz ./* && popd diff --git a/package.json b/package.json new file mode 100644 index 0000000..6c14c2c --- /dev/null +++ b/package.json @@ -0,0 +1,86 @@ +{ + "name": "osmos", + "version": "2.0.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint", + "dev": "vue-cli-service serve --open" + }, + "dependencies": { + "core-js": "^3.6.5", + "css-doodle": "^0.12.0", + "lodash": "^4.17.21", + "mousetrap": "^1.6.5", + "nouislider": "^14.6.3", + "parallax-js": "^3.1.0", + "vue": "^2.6.12", + "vue-color": "^2.8.1", + "vue-i18n": "^8.22.2", + "vue-router": "^3.2.0", + "vuex": "^3.4.0" + }, + "devDependencies": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/plugin-proposal-optional-chaining": "^7.9.0", + "@types/lodash": "^4.14.178", + "@types/mousetrap": "^1.6.9", + "@types/node": "14.10.0", + "@typescript-eslint/eslint-plugin": "^2.33.0", + "@typescript-eslint/parser": "^2.33.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "@vue/cli-plugin-babel": "~4.5.0", + "@vue/cli-plugin-eslint": "~4.5.0", + "@vue/cli-plugin-router": "~4.5.0", + "@vue/cli-plugin-typescript": "~4.5.0", + "@vue/cli-plugin-vuex": "~4.5.0", + "@vue/cli-service": "~4.5.0", + "@vue/compiler-sfc": "^3.1.4", + "@vue/eslint-config-airbnb": "^5.0.2", + "@vue/eslint-config-prettier": "^6.0.0", + "@vue/eslint-config-standard": "^5.1.2", + "@vue/eslint-config-typescript": "^5.0.2", + "babel-core": "7.0.0-bridge.0", + "babel-eslint": "^10.0.3", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-vue-jsx": "3.3.0", + "babel-preset-env": "^1.7.0", + "eslint": "^6.7.2", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-prettier": "^3.1.3", + "eslint-plugin-promise": "^4.2.1", + "eslint-plugin-standard": "^4.0.0", + "eslint-plugin-vue": "^6.2.2", + "husky": "^4.2.5", + "lint-staged": "^10.2.2", + "prettier": "^1.19.1", + "stylus": "^0.54.8", + "stylus-loader": "^3.0.2", + "typescript": "^4.5.4", + "vue-class-component": "^7.2.3", + "vue-property-decorator": "^8.4.2", + "vue-template-compiler": "^2.6.11", + "vuex-module-decorators": "^0.17.0", + "webpack-glsl-loader": "^1.0.1" + }, + "gitHooks": { + "pre-commit": "lint-staged" + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "*.{js,vue}": [ + "eslint --quiet" + ], + "*.{js,jsx,vue,ts,tsx}": [ + "vue-cli-service lint", + "git add" + ] + } +} diff --git a/pre-build.sh b/pre-build.sh new file mode 100644 index 0000000..a9cfba4 --- /dev/null +++ b/pre-build.sh @@ -0,0 +1,4 @@ +# npm run build +sudo chmod 777 -R dist +rm -Rf /usr/share/lightdm-webkit/themes/lightdm-webkit-theme-osmos/* +cp -r ./dist/* /usr/share/lightdm-webkit/themes/lightdm-webkit-theme-osmos \ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..30feec6 --- /dev/null +++ b/public/index.html @@ -0,0 +1,24 @@ + + + + + + + Osmos + + + + + + +
+ + + + diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..975ddaf --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,91 @@ +import { Component, Vue } from 'vue-property-decorator' +import Mousetrap from 'mousetrap' +import { AppModule } from '@/store/app' +import { PageModule } from './store/page' + +@Component +export default class MainApp extends Vue { + get bodyClass() { + return AppModule.bodyClass + } + + created() { + AppModule.setUpSettings() + this.initKeybinds() + } + + initKeybinds() { + const modKey = 'fn' + + Mousetrap.bind(`${modKey}+t`, () => { + PageModule.openTab({ type: 'themes' }) + PageModule.openBlock({ id: 'settings' }) + }) + + Mousetrap.bind(`${modKey}+c`, () => { + PageModule.openTab({ type: 'custom' }) + PageModule.openBlock({ id: 'settings' }) + }) + + Mousetrap.bind(`${modKey}+s`, () => { + PageModule.openTab({ type: 'settings' }) + PageModule.openBlock({ id: 'settings' }) + }) + + Mousetrap.bind('escape', () => { + const isFocusPassword = document.querySelector('#password:focus') as HTMLInputElement + console.log({ isFocusPassword }) + console.log('Escape') + + if (isFocusPassword) { + isFocusPassword.blur() + } else if (PageModule.menu.view) { + PageModule.ASSIGN_MENU({ view: false }) + } else if (PageModule.activeBlock) { + PageModule.closeBlock() + } + }) + + Mousetrap.bind('enter', () => { + const isFocusPassword = document.querySelector('#password:focus') + const inputPassword = document.querySelector('#password') as HTMLInputElement + + if (isFocusPassword) { + AppModule.login() + } else if (inputPassword) { + inputPassword.focus() + } + }) + + // keyPress(event: KeyboardEvent) { + // if (PageModule.activeBlocks.length === 0) { + // PageModule.openBlock({ id: 'login' }) + // } + // } + + Mousetrap.bind(`${modKey}+h`, () => { + console.log('hide all windows') + PageModule.CLOSE_ALL_ACTIVE_BLOCK() + }) + + Mousetrap.bind(`${modKey}+p`, () => { + console.log('power off') + }) + + Mousetrap.bind(`${modKey}+r`, () => { + console.log('restart') + }) + + Mousetrap.bind(`${modKey}+R`, () => { + AppModule.randomizeSettingsTheme() + }) + } + + render() { + return ( +
+ +
+ ) + } +} diff --git a/src/assets/font/digital.ttf b/src/assets/font/digital.ttf new file mode 100644 index 0000000..5dbe6f9 Binary files /dev/null and b/src/assets/font/digital.ttf differ diff --git a/src/assets/images/themes/destruction/index.png b/src/assets/images/themes/destruction/index.png new file mode 100644 index 0000000..1ed22f4 Binary files /dev/null and b/src/assets/images/themes/destruction/index.png differ diff --git a/src/assets/images/themes/infinity/index.png b/src/assets/images/themes/infinity/index.png new file mode 100644 index 0000000..386cb71 Binary files /dev/null and b/src/assets/images/themes/infinity/index.png differ diff --git a/src/assets/images/themes/learn/index.png b/src/assets/images/themes/learn/index.png new file mode 100644 index 0000000..ff4af9f Binary files /dev/null and b/src/assets/images/themes/learn/index.png differ diff --git a/src/assets/images/themes/magic/index.png b/src/assets/images/themes/magic/index.png new file mode 100644 index 0000000..cc36aea Binary files /dev/null and b/src/assets/images/themes/magic/index.png differ diff --git a/src/assets/images/themes/malevich/bottom-left-pattern.svg b/src/assets/images/themes/malevich/bottom-left-pattern.svg new file mode 100755 index 0000000..f0a0281 --- /dev/null +++ b/src/assets/images/themes/malevich/bottom-left-pattern.svg @@ -0,0 +1 @@ +ps-bottom-left-pattern \ No newline at end of file diff --git a/src/assets/images/themes/malevich/bottom-left.svg b/src/assets/images/themes/malevich/bottom-left.svg new file mode 100755 index 0000000..15af600 --- /dev/null +++ b/src/assets/images/themes/malevich/bottom-left.svg @@ -0,0 +1 @@ +ps-bottom-left \ No newline at end of file diff --git a/src/assets/images/themes/malevich/bottom-right-triangle.svg b/src/assets/images/themes/malevich/bottom-right-triangle.svg new file mode 100755 index 0000000..dcf5d41 --- /dev/null +++ b/src/assets/images/themes/malevich/bottom-right-triangle.svg @@ -0,0 +1 @@ +ps-bottom-right-triangle \ No newline at end of file diff --git a/src/assets/images/themes/malevich/bottom-right.svg b/src/assets/images/themes/malevich/bottom-right.svg new file mode 100755 index 0000000..94fefec --- /dev/null +++ b/src/assets/images/themes/malevich/bottom-right.svg @@ -0,0 +1 @@ +ps-bottom-right \ No newline at end of file diff --git a/src/assets/images/themes/malevich/index.png b/src/assets/images/themes/malevich/index.png new file mode 100644 index 0000000..ba4659e Binary files /dev/null and b/src/assets/images/themes/malevich/index.png differ diff --git a/src/assets/images/themes/malevich/top-left-multi.svg b/src/assets/images/themes/malevich/top-left-multi.svg new file mode 100755 index 0000000..e385d34 --- /dev/null +++ b/src/assets/images/themes/malevich/top-left-multi.svg @@ -0,0 +1 @@ +ps-top-left-multi \ No newline at end of file diff --git a/src/assets/images/themes/malevich/top-left.svg b/src/assets/images/themes/malevich/top-left.svg new file mode 100755 index 0000000..3dbdea4 --- /dev/null +++ b/src/assets/images/themes/malevich/top-left.svg @@ -0,0 +1 @@ +ps-top-left \ No newline at end of file diff --git a/src/assets/images/themes/malevich/top-right-circle.svg b/src/assets/images/themes/malevich/top-right-circle.svg new file mode 100755 index 0000000..93f1fe9 --- /dev/null +++ b/src/assets/images/themes/malevich/top-right-circle.svg @@ -0,0 +1 @@ +ps-top-right-circle \ No newline at end of file diff --git a/src/assets/images/themes/malevich/top-right.svg b/src/assets/images/themes/malevich/top-right.svg new file mode 100755 index 0000000..3173f75 --- /dev/null +++ b/src/assets/images/themes/malevich/top-right.svg @@ -0,0 +1 @@ +ps-top-right \ No newline at end of file diff --git a/src/assets/images/themes/osmos/MountainBack_Left.svg b/src/assets/images/themes/osmos/MountainBack_Left.svg new file mode 100755 index 0000000..08ac300 --- /dev/null +++ b/src/assets/images/themes/osmos/MountainBack_Left.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/themes/osmos/MountainBack_Right.svg b/src/assets/images/themes/osmos/MountainBack_Right.svg new file mode 100755 index 0000000..fa189ff --- /dev/null +++ b/src/assets/images/themes/osmos/MountainBack_Right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/osmos/MountainFront_Left.svg b/src/assets/images/themes/osmos/MountainFront_Left.svg new file mode 100755 index 0000000..d377f8d --- /dev/null +++ b/src/assets/images/themes/osmos/MountainFront_Left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/osmos/MountainFront_Right.svg b/src/assets/images/themes/osmos/MountainFront_Right.svg new file mode 100755 index 0000000..c503db3 --- /dev/null +++ b/src/assets/images/themes/osmos/MountainFront_Right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/osmos/MountainSecond_Left.svg b/src/assets/images/themes/osmos/MountainSecond_Left.svg new file mode 100755 index 0000000..be3343a --- /dev/null +++ b/src/assets/images/themes/osmos/MountainSecond_Left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/osmos/MountainSecond_Right.svg b/src/assets/images/themes/osmos/MountainSecond_Right.svg new file mode 100755 index 0000000..8e53f79 --- /dev/null +++ b/src/assets/images/themes/osmos/MountainSecond_Right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/osmos/index.png b/src/assets/images/themes/osmos/index.png new file mode 100755 index 0000000..c6015bf Binary files /dev/null and b/src/assets/images/themes/osmos/index.png differ diff --git a/src/assets/images/themes/osmos/stars.svg b/src/assets/images/themes/osmos/stars.svg new file mode 100755 index 0000000..c899ad8 --- /dev/null +++ b/src/assets/images/themes/osmos/stars.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/themes/osmos/sun.svg b/src/assets/images/themes/osmos/sun.svg new file mode 100755 index 0000000..0ad7bdf --- /dev/null +++ b/src/assets/images/themes/osmos/sun.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/themes/planet/index.png b/src/assets/images/themes/planet/index.png new file mode 100644 index 0000000..3ed9e83 Binary files /dev/null and b/src/assets/images/themes/planet/index.png differ diff --git a/src/assets/images/themes/plasma/index.png b/src/assets/images/themes/plasma/index.png new file mode 100644 index 0000000..6591e69 Binary files /dev/null and b/src/assets/images/themes/plasma/index.png differ diff --git a/src/assets/images/themes/random/index.png b/src/assets/images/themes/random/index.png new file mode 100644 index 0000000..779d8e8 Binary files /dev/null and b/src/assets/images/themes/random/index.png differ diff --git a/src/assets/images/themes/rings/index.png b/src/assets/images/themes/rings/index.png new file mode 100644 index 0000000..c022441 Binary files /dev/null and b/src/assets/images/themes/rings/index.png differ diff --git a/src/assets/images/themes/space/Hole.svg b/src/assets/images/themes/space/Hole.svg new file mode 100755 index 0000000..0eb2735 --- /dev/null +++ b/src/assets/images/themes/space/Hole.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/themes/space/MainPlanet.svg b/src/assets/images/themes/space/MainPlanet.svg new file mode 100755 index 0000000..20bff21 --- /dev/null +++ b/src/assets/images/themes/space/MainPlanet.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/themes/space/Meteor.svg b/src/assets/images/themes/space/Meteor.svg new file mode 100755 index 0000000..bfe5656 --- /dev/null +++ b/src/assets/images/themes/space/Meteor.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/images/themes/space/UFO.svg b/src/assets/images/themes/space/UFO.svg new file mode 100755 index 0000000..1383e4c --- /dev/null +++ b/src/assets/images/themes/space/UFO.svg @@ -0,0 +1 @@ + diff --git a/src/assets/images/themes/space/YellowPlanet.svg b/src/assets/images/themes/space/YellowPlanet.svg new file mode 100755 index 0000000..d718391 --- /dev/null +++ b/src/assets/images/themes/space/YellowPlanet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/themes/space/index.png b/src/assets/images/themes/space/index.png new file mode 100644 index 0000000..2c25099 Binary files /dev/null and b/src/assets/images/themes/space/index.png differ diff --git a/src/assets/images/themes/tenderness/index.png b/src/assets/images/themes/tenderness/index.png new file mode 100644 index 0000000..7864649 Binary files /dev/null and b/src/assets/images/themes/tenderness/index.png differ diff --git a/src/components/app/AppButton.tsx b/src/components/app/AppButton.tsx new file mode 100644 index 0000000..0bc2b7a --- /dev/null +++ b/src/components/app/AppButton.tsx @@ -0,0 +1,114 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' +import AppIcon from '@/components/app/AppIcon.vue' +import { Route } from 'vue-router' +import { CreateElement, VNode, VNodeData } from 'vue' +import { RenderContext } from 'vue/types/umd' +import { spawn } from 'child_process' + +const prefix = 'app-button' + +interface AppButtonPropsInterface { + label: string; + icon?: string; + fab?: boolean; + tag?: string; + link?: boolean; + nuxt?: boolean; + block?: boolean; + target?: string; + disabled?: boolean; + loading?: boolean; + type?: string; + to?: Route | string; + href?: Route | string; +} + +@Component({ + components: { + AppIcon + } +}) +export default class AppButton extends Vue implements AppButtonPropsInterface { + @Prop({ type: String }) label!: string + @Prop({ type: String }) icon!: string + @Prop({ type: String }) type!: string + @Prop({ type: Boolean }) fab!: boolean + @Prop({ type: Boolean }) link!: boolean + @Prop({ type: Boolean }) block!: boolean + @Prop({ type: String }) target!: string + @Prop({ type: Boolean }) loading!: boolean + @Prop({ type: Boolean }) nuxt!: boolean + @Prop({ type: Boolean }) disabled!: boolean + @Prop({ type: String, default: 'button' }) tag!: string + @Prop({ type: [Object, String] }) to!: Route | string + @Prop({ type: [Object, String] }) href!: Route | string + + get classes() { + const classes: any = { [prefix]: true } + const propertyList: Array = ['fab', 'block'] + + propertyList.forEach(property => { + classes[`${prefix}--${property}`] = this[property] + }) + + return { + ...classes + } + } + + get isLink() { + return this.to || this.href || this.link + } + + generateRouteLink() { + let tag + + const data: VNodeData = { + attrs: { + tabindex: 'tabindex' in this.$attrs ? this.$attrs.tabindex : undefined + }, + class: this.classes, + [this.to ? 'nativeOn' : 'on']: { + ...this.$listeners + }, + ref: 'link' + } + + if (this.to) { + tag = this.nuxt ? 'nuxt-link' : 'router-link' + } else { + tag = (this.href && 'a') || this.tag || 'div' + + if (tag === 'a' && this.href) data.attrs!.href = this.href + } + + if (this.target) data.attrs!.target = this.target + + return { tag, data } + } + + buildContent() { + return { this.$slots.default } + } + + buildLoader() { + return + { this.$slots.loader || } + + } + + render(h: CreateElement): VNode { + const children = [ + this.buildContent(), + this.loading && this.buildLoader() + ] + const { tag, data } = this.generateRouteLink() + + if (tag === 'button') { + data.attrs!.type = this.type + data.attrs!.disabled = this.disabled + } + + return h(tag, data, children) + } +} diff --git a/src/components/app/AppCheckbox.tsx b/src/components/app/AppCheckbox.tsx new file mode 100644 index 0000000..e2700d7 --- /dev/null +++ b/src/components/app/AppCheckbox.tsx @@ -0,0 +1,42 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' +import AppIcon from '@/components/app/AppIcon.vue' + +@Component({ + components: { AppIcon } +}) +export default class AppCheckbox extends Vue { + @Prop({ type: Boolean, default: false }) value!: boolean + @Prop({ default: '' }) label!: string + + get isActive() { + return this.value + } + + get idCheckbox() { + return `input-${(this as any)._uid}` + } + + changeState() { + this.$emit('input', !this.value) + } + + render() { + return + } +} diff --git a/src/components/app/AppColorSelector.tsx b/src/components/app/AppColorSelector.tsx new file mode 100644 index 0000000..430bd73 --- /dev/null +++ b/src/components/app/AppColorSelector.tsx @@ -0,0 +1,36 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' + +// eslint-disable-next-line @typescript-eslint/ban-ts-ignore +// @ts-ignore +import { Chrome } from 'vue-color' + +@Component({ + components: { Chrome } +}) +export default class AppColorSelector extends Vue { + @Prop({ type: String, default: '#fff' }) value!: string + @Prop({ default: '' }) label!: string + + changeState(event: Event) { + event.stopPropagation() + + this.$emit('input', !this.value) + } + + render() { + return
+

{ this.$t(this.label) }

+ + +
+ } +} diff --git a/src/components/app/AppIcon.vue b/src/components/app/AppIcon.vue new file mode 100644 index 0000000..6bb4c84 --- /dev/null +++ b/src/components/app/AppIcon.vue @@ -0,0 +1,933 @@ + + + diff --git a/src/components/app/AppMenu.tsx b/src/components/app/AppMenu.tsx new file mode 100644 index 0000000..79f6cbe --- /dev/null +++ b/src/components/app/AppMenu.tsx @@ -0,0 +1,112 @@ +import { Component, Prop, Vue, Watch } from 'vue-property-decorator' +import { PageModule } from '@/store/page' +import AppIcon from '@/components/app/AppIcon.vue' +import { AppMenuItem, AppMenuPosition } from '@/models/page' + +@Component({ + components: { AppIcon } +}) +export default class AppMenu extends Vue { + isActive = false + innerPositioned: AppMenuPosition = { left: 0, top: 0, width: 199 } + + get position() { + return this.menu.position ?? this.innerPositioned + } + + get menu() { + const menu = PageModule.menu + return menu + } + + @Watch('menu.view') + updatePosition(isOpen: boolean) { + if (isOpen) { + this.calculatePosition() + } + } + + get style() { + return Object.entries(this.position).reduce((acc, [property, value]) => { + acc += `${property}: ${value}px;` + return acc + }, '') + } + + get menuNode() { + return this.$refs.menu as Element + } + + async handleCallback(item: AppMenuItem | string) { + if (this.menu.handler) { + await this.menu.handler(item) + } + + this.closeMenu() + } + + mounted() { + window.addEventListener('resize', this.handleResize, { passive: true }) + this.menuNode && this.menuNode.addEventListener('mousedown', this.stopPreventEvent, { passive: true }) + } + + beforeDestroy() { + window.removeEventListener('resize', this.handleResize) + this.menuNode && this.menuNode.removeEventListener('mousedown', this.stopPreventEvent) + } + + handleResize() { + this.calculatePosition() + } + + calculatePosition() { + const node = this.menu.node + + if (!node || !this.menu.view) return + + const { bottom, left, top, height, width } = node.getBoundingClientRect() + const allHeight = window.innerHeight + + const positionY = bottom > top ? 'bottom' : 'top' + const position: AppMenuPosition = { left, width } + + if (positionY === 'bottom') { + position.top = top + height + } else { + position.bottom = allHeight - bottom + height + } + + this.innerPositioned = position + } + + closeMenu() { + PageModule.ASSIGN_MENU({ view: false }) + } + + stopPreventEvent(event: Event) { + event.stopPropagation() + event.preventDefault() + } + + buildElementItem(item: AppMenuItem, index: number) { + return + } + + render() { + return + } +} diff --git a/src/components/app/AppPaletteSelector.tsx b/src/components/app/AppPaletteSelector.tsx new file mode 100644 index 0000000..77832ab --- /dev/null +++ b/src/components/app/AppPaletteSelector.tsx @@ -0,0 +1,32 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' +import AppIcon from '@/components/app/AppIcon.vue' + +@Component({ + components: { AppIcon } +}) +export default class AppPaletteSelector extends Vue { + @Prop({ type: Number, default: 0 }) value!: number + @Prop({ type: Array, required: true }) values!: string[][] + @Prop({ default: '' }) label!: string + + activatePalette(index: number) { + this.$emit('input', index) + } + + generatePaletteItem(colors: string[], index: number) { + const buildColor = (color: string) =>
+ const classes = `palette-block ${index === this.value ? 'active' : ''}` + + return
this.activatePalette(index) } class={ classes }> { colors.map(buildColor) }
+ } + + generatePaletteItems() { + return this.values.map(this.generatePaletteItem) + } + + render() { + return
+ { this.generatePaletteItems() } +
+ } +} diff --git a/src/components/app/AppSelector.tsx b/src/components/app/AppSelector.tsx new file mode 100644 index 0000000..8120c8c --- /dev/null +++ b/src/components/app/AppSelector.tsx @@ -0,0 +1,89 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' + +import { PageModule } from '@/store/page' +import AppIcon from '@/components/app/AppIcon.vue' +import { AppMenuItem } from '@/models/page' + +export interface AppSelectorProps { + value: AppMenuItem | string | undefined; + items: AppMenuItem[]; + label: string; +} +@Component({ + components: { AppIcon } +}) +export default class AppSelector extends Vue implements AppSelectorProps { + @Prop({ type: [Object, String], default: undefined }) value!: AppMenuItem | string | undefined + @Prop({ type: [Array], default: () => [] }) items!: AppMenuItem[] + @Prop({ default: '' }) label!: string + + selectedValue: null | AppMenuItem | string = null + + get fullItem() { + const selected = this.value !== null ? this.value : this.selectedValue + + return this.items.find(({ value }) => { + const finalValue = typeof selected === 'object' ? selected?.value : selected + return value === finalValue + }) + } + + get menu() { + return PageModule.menu + } + + get isActive() { + return this.menu?.view && this.menu?.node === this.$refs.selector + } + + get currentValueLabel() { + const selected = this.fullItem ?? this.selectedValue + return typeof selected === 'object' ? selected?.text : selected + } + + openSelector(event: Event) { + event.preventDefault() + event.stopPropagation() + + if (this.menu.view) { + return PageModule.ASSIGN_MENU({ view: false }) + } + + PageModule.ASSIGN_MENU({ + node: this.$refs.selector as HTMLElement, + view: true, + items: this.items, + handler: this.callback + }) + } + + callback(item: AppMenuItem | string) { + const finalValue = typeof item === 'object' ? item.value : item + + this.selectedValue = item + this.$emit('input', finalValue) + } + + render() { + const selectorIcon = this.fullItem?.icon ? : null + + return
+

+ { selectorIcon } +

+ { this.currentValueLabel || this.label } +

+

+ +
+ } +} diff --git a/src/components/app/AppSlider.tsx b/src/components/app/AppSlider.tsx new file mode 100644 index 0000000..fc989a0 --- /dev/null +++ b/src/components/app/AppSlider.tsx @@ -0,0 +1,70 @@ +import { Component, Prop, Vue } from 'vue-property-decorator' + +// eslint-disable-next-line @typescript-eslint/ban-ts-ignore +// @ts-ignore +import noUiSlider from 'nouislider' +import { Debounce } from '@/utils/helper' +@Component +export default class AppSlider extends Vue { + @Prop({ type: [Number], default: '' }) value!: number | string + @Prop({ default: '' }) label!: string + @Prop({ type: Number, default: 0 }) from!: number + @Prop({ type: Number, default: 100 }) to!: number + @Prop({ type: Number, default: 1 }) step!: number + @Prop({ type: Boolean, default: false }) changeOnUpdate!: boolean + + preValue = 0 + + @Debounce(100) + syncSliderValue() { + const slider = (this.$refs.slider as any).noUiSlider + const sliderValue = parseFloat(slider.get()) + const isDifferentValues = sliderValue !== this.value + + if (isDifferentValues) { + slider.set(this.value) + } + } + + updated() { + this.syncSliderValue() + } + + mounted() { + const slider = this.$refs.slider as any + const isValidValue = this.value !== 'undefined' + + noUiSlider.create(slider, { + start: isValidValue ? this.value : this.to, + connect: 'lower', + step: this.step, + range: { + min: this.from, + max: this.to + } + }) + + slider.noUiSlider.on('slide', (values: string[]) => { + this.preValue = parseFloat(values[0]) + }) + + const eventChange = this.changeOnUpdate ? 'update' : 'set' + + slider.noUiSlider.on(eventChange, (values: string[]) => { + const value = parseFloat(values[0]) + + this.$emit('input', value) + }) + } + + render() { + return
+
+

{ this.$t(this.label) }

+
+
+
+
+
+ } +} diff --git a/src/components/base/BackgroundImage.tsx b/src/components/base/BackgroundImage.tsx new file mode 100644 index 0000000..eb7d068 --- /dev/null +++ b/src/components/base/BackgroundImage.tsx @@ -0,0 +1,40 @@ +import { Component, Vue } from 'vue-property-decorator' +import { CreateElement } from 'vue/types/umd' + +import { AppModule } from '@/store/app' +import { PageModule } from '@/store/page' + +const components: { [k: string]: any } = {} + +const requireComponent = require.context( + '@/components/themes', true, /\.(vue|tsx)$/ +) + +requireComponent.keys().forEach(fileName => { + let componentName = fileName.replace(/^\.\//, '').replace(/\.\w+$/, '') + const isInFolder = componentName.includes('/') + + if (isInFolder) { + componentName = componentName.split('/')[0] + } + + const componentConfig = requireComponent(fileName) + components[componentName] = componentConfig.default || componentConfig +}) + +@Component({ components }) +export default class BackgroundImage extends Vue { + get theme() { + return AppModule.activeTheme + } + + get isOpenLogin() { + return PageModule.isOpenBlock('login') + } + + render(h: CreateElement) { + const body = h(components[this.theme.component ?? 'random']) + + return
{ body }
+ } +} diff --git a/src/components/base/FrameRateBlock.tsx b/src/components/base/FrameRateBlock.tsx new file mode 100644 index 0000000..b3ce760 --- /dev/null +++ b/src/components/base/FrameRateBlock.tsx @@ -0,0 +1,46 @@ +import { AppModule } from '@/store/app' +import { Component, Vue } from 'vue-property-decorator' + +let elapsedTime = 0 +let frameCount = 0 +let lastTime = new Date().getTime() + +@Component +export default class FrameRateBlock extends Vue { + FPS = 0 + + get isShow() { + return AppModule.showFrameRate + } + + mounted() { + this.FPS = 0 + this.drawScene() + lastTime = new Date().getTime() + } + + drawScene() { + const now = new Date().getTime() + + frameCount++ + elapsedTime += (now - lastTime) + + lastTime = now + + if (elapsedTime >= 1000) { + const fps = frameCount + frameCount = 0 + elapsedTime -= 1000 + + this.FPS = fps + } + + if (this.isShow) { + requestAnimationFrame(this.drawScene) + } + } + + render() { + return
FPS: { this.FPS }
+ } +} diff --git a/src/components/base/GithubButton.tsx b/src/components/base/GithubButton.tsx new file mode 100644 index 0000000..5a0c847 --- /dev/null +++ b/src/components/base/GithubButton.tsx @@ -0,0 +1,21 @@ +import { Component, Vue } from 'vue-property-decorator' + +import AppIcon from '@/components/app/AppIcon.vue' +import AppButton from '@/components/app/AppButton' + +@Component({ + components: { AppIcon, AppButton }, + funtional: true +}) +export default class GithubButton extends Vue { + render() { + return + + + } +} diff --git a/src/components/base/LoginComponent.tsx b/src/components/base/LoginComponent.tsx new file mode 100644 index 0000000..30b0c16 --- /dev/null +++ b/src/components/base/LoginComponent.tsx @@ -0,0 +1,82 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' +import { PageModule } from '@/store/page' + +import AppIcon from '@/components/app/AppIcon.vue' +import UserAvatar from '@/components/base/UserAvatar' +import UserInput from '@/components/base/UserInput' +import SettingsView from '@/components/base/settings/SettingsView' + +@Component({ + components: { + AppIcon, + UserInput, + UserAvatar, + SettingsView + } +}) +export default class LoginComponent extends Vue { + get theme() { + return AppModule.activeTheme + } + + get classObject() { + return { + 'login-menu': true + } + } + + get activeBlock() { + return PageModule.activeBlock + } + + get currentDesktop() { + return AppModule.currentDesktop + } + + get mainTabIndex() { + return PageModule.mainTabIndex + } + + get tabs() { + const tabs = [this.$t('settings.choice-themes'), this.$t('settings.general')] + const hasThemeSettings = AppModule.activeTheme?.settings?.length !== undefined + + if (hasThemeSettings) { + tabs.splice(1, 0, this.$t('settings.customize-theme')) + } + + return tabs + } + + activateTab(index: number) { + PageModule.SET_STATE_PAGE({ key: 'mainTabIndex', value: index }) + } + + openSettings(event: Event) { + event.preventDefault() + event.stopPropagation() + + PageModule.openBlock({ id: 'settings' }) + } + + render() { + const nativeOn = { + click: (event: Event) => { + this.openSettings(event) + PageModule.openTab({ type: 'settings' }) + } + } + + return
+
+ + + {/* nativeOn */} + + +
+
+ } +} diff --git a/src/components/base/SettingsComponent.tsx b/src/components/base/SettingsComponent.tsx new file mode 100644 index 0000000..caa4047 --- /dev/null +++ b/src/components/base/SettingsComponent.tsx @@ -0,0 +1,89 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' +import { PageModule } from '@/store/page' + +import AppIcon from '@/components/app/AppIcon.vue' +import UserAvatar from '@/components/base/UserAvatar' +import SettingsView from '@/components/base/settings/SettingsView' + +@Component({ + components: { + AppIcon, + UserAvatar, + SettingsView + } +}) +export default class SettingsComponent extends Vue { + get theme() { + return AppModule.activeTheme + } + + get classObject() { + return { + 'login-menu': true + } + } + + get activeBlock() { + return PageModule.activeBlock + } + + get currentDesktop() { + return AppModule.currentDesktop + } + + get mainTabIndex() { + return PageModule.mainTabIndex + } + + get tabs() { + const tabs = [this.$t('settings.choice-themes').toString(), this.$t('settings.general').toString()] + const hasThemeSettings = AppModule.activeTheme?.settings?.length !== undefined + + if (hasThemeSettings) { + tabs.splice(1, 0, this.$t('settings.customize-theme').toString()) + } + + return tabs + } + + get isViewThemeOnly() { + return AppModule.viewThemeOnly + } + + activateTab(index: number) { + PageModule.SET_STATE_PAGE({ key: 'mainTabIndex', value: index }) + } + + openLogin(event: Event) { + event.preventDefault() + event.stopPropagation() + + PageModule.openBlock({ id: 'login' }) + } + + generateTab(tab: string, index: number) { + const classes = `user-settings-tab ${this.mainTabIndex === index ? 'active' : ''}` + + return
this.activateTab(index)}> { tab }
+ } + + generateTabs() { + return + } + + render() { + return + } +} diff --git a/src/components/base/ShutdownButton.tsx b/src/components/base/ShutdownButton.tsx new file mode 100644 index 0000000..2a652d6 --- /dev/null +++ b/src/components/base/ShutdownButton.tsx @@ -0,0 +1,33 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { PageModule } from '@/store/page' +import AppIcon from '@/components/app/AppIcon.vue' +import ShutdownMenu from '@/components/base/ShutdownMenu' +import { appWindow } from '@/models/lightdm' + +@Component({ + components: { AppIcon, ShutdownMenu } +}) +export default class ShutdownBlock extends Vue { + get isShow() { + return !!PageModule.activeBlock + } + + shutdown(event: MouseEvent) { + event.stopPropagation() + appWindow.lightdm.suspend() + } + + render() { + const button =
+ +
+ +
+
+ + return + { this.isShow ? button : null } + + } +} diff --git a/src/components/base/ShutdownMenu.tsx b/src/components/base/ShutdownMenu.tsx new file mode 100644 index 0000000..32daee4 --- /dev/null +++ b/src/components/base/ShutdownMenu.tsx @@ -0,0 +1,39 @@ +import { Component, Vue } from 'vue-property-decorator' + +import AppIcon from '@/components/app/AppIcon.vue' +import { appWindow } from '@/models/lightdm' + +@Component({ + components: { AppIcon } +}) +export default class ShutdownMenu extends Vue { + get actions() { + return [ + { + icon: 'restart', + show: appWindow.lightdm.can_restart, + callback: appWindow.lightdm.restart + }, + { + icon: 'suspend', + show: appWindow.lightdm.can_suspend, + callback: appWindow.lightdm.suspend + }, + { + icon: 'hibernate', + show: appWindow.lightdm.can_hibernate, + callback: appWindow.lightdm.hibernate + } + ].filter(({ show }) => show) + } + + render() { + return
+ { this.actions.map((action) => { + return
+ +
+ })} +
+ } +} diff --git a/src/components/base/UserAvatar.tsx b/src/components/base/UserAvatar.tsx new file mode 100644 index 0000000..b04a488 --- /dev/null +++ b/src/components/base/UserAvatar.tsx @@ -0,0 +1,78 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' +import AppIcon from '@/components/app/AppIcon.vue' +import { LightdmUsers } from '@/models/lightdm' +import { PageModule } from '@/store/page' +import { DateTimeFormatOptions } from 'vue-i18n' + +@Component({ + components: { AppIcon } +}) +export default class UserAvatar extends Vue { + updater = new Date().getTime() + + get currentTime() { + const options: DateTimeFormatOptions = { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + hour12: false + } + + if (this.isOpenSettings) { + options.month = 'long' + options.day = 'numeric' + options.weekday = 'short' + } + + return new Intl.DateTimeFormat(this.locale, options).format(new Date()) + } + + get isOpenSettings() { + return PageModule.isOpenBlock('settings') + } + + get locale() { + return PageModule.locale + } + + get user() { + return AppModule.currentUser + } + + get users() { + return AppModule.users + } + + mounted() { + this.updater = new Date().getTime() + + setInterval(() => { this.updater = new Date().getTime() }, 1000) + } + + render() { + const buildUserAvatar = (image: string | undefined) => { + const defaultAvatar = + const userAvatar =
+ + return image ? userAvatar : defaultAvatar + } + + const buildUser = (user: LightdmUsers) => { + return
+

{ this.currentTime }

+ { buildUserAvatar(user?.image) } +
{ user?.display_name }
+
+ } + + return + { this.users.filter(({ username }) => username === this.user?.username).map(buildUser) } + + } +} diff --git a/src/components/base/UserInput.tsx b/src/components/base/UserInput.tsx new file mode 100644 index 0000000..44d2617 --- /dev/null +++ b/src/components/base/UserInput.tsx @@ -0,0 +1,85 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' +import AppIcon from '@/components/app/AppIcon.vue' +import { PageModule } from '@/store/page' + +@Component({ + components: { AppIcon } +}) +export default class UserInput extends Vue { + logging = false + viewPassword = false + + get user() { + return AppModule.currentUser + } + + get passwordValue() { + return AppModule.password + } + + login() { + AppModule.login() + } + + handleKeyup(event: InputEvent) { + AppModule.SET_STATE_APP({ key: 'password', value: (event.target as HTMLInputElement)?.value || '' }) + } + + toggleShowPassword() { + this.viewPassword = !this.viewPassword + } + + openSettings(event: Event) { + event.preventDefault() + event.stopPropagation() + + PageModule.openBlock({ id: 'settings' }) + } + + render() { + return
+ {/* */} + + + + + + + +
+ } +} diff --git a/src/components/base/settings/SettingsCustom.tsx b/src/components/base/settings/SettingsCustom.tsx new file mode 100644 index 0000000..48e4759 --- /dev/null +++ b/src/components/base/settings/SettingsCustom.tsx @@ -0,0 +1,120 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' +import { AppInputButton, AppInputColor, AppInputThemeGeneral, AppInputThemeSlider, AppTheme } from '@/models/app' + +import AppSlider from '@/components/app/AppSlider' +import AppButton from '@/components/app/AppButton' +import AppCheckbox from '@/components/app/AppCheckbox' +import AppSelector from '@/components/app/AppSelector' +import AppColorSelector from '@/components/app/AppColorSelector' +import AppPaletteSelector from '@/components/app/AppPaletteSelector' +import { Debounce } from '@/utils/helper' +import { Route } from 'vue-router/types/router' + +@Component({ + components: { + AppSlider, + AppButton, + AppSelector, + AppColorSelector, + AppPaletteSelector + } +}) +export default class SettingsCustom extends Vue { + syncThemeWithQuery = true + + get theme() { + return AppModule.activeTheme as AppTheme + } + + get inputs() { + return this.theme.settings || [] + } + + @Debounce() + updateQuery() { + if (this.syncThemeWithQuery) { + // console.log('AppModule.syncStoreWithQuery') + AppModule.syncStoreWithQuery() + } + } + + buildSlider(input: AppInputThemeSlider) { + const { label, value, options: { step, min: from, max: to, changeOnUpdate } } = input + const props = { to, step, from, label, value, changeOnUpdate } + const handler = (value: number) => { + AppModule.changeSettingsThemeInput({ key: input.name, value }) + + if (input.callback) { + input.callback(value) + } + } + + return + } + + buildColor(input: AppInputThemeGeneral) { + const { label, value, options } = input + const props = { label, value } + const handler = (color: AppInputColor) => { + AppModule.changeSettingsThemeInput({ key: input.name, value: color.hex }) + + if (input.callback) { + input.callback(color.hex) + } + } + + return + } + + buildCheckbox(input: AppInputThemeGeneral) { + const { label, value } = input + const props = { label, value } + const handler = (value: boolean) => { + AppModule.changeSettingsThemeInput({ key: input.name, value }) + + if (input.callback) { + input.callback(value) + } + } + + return + } + + buildPalette(input: AppInputThemeGeneral) { + const { label, value, values } = input + const props = { label, value, values } + const handler = (value: boolean) => { + AppModule.changeSettingsThemeInput({ key: input.name, value }) + } + + return + } + + buildButton(input: AppInputButton) { + return { this.$t(input.label) } + } + + buildUnknown(input: AppInputThemeGeneral) { + return
Doesn't exist input - { input.type }
+ } + + updated() { + this.updateQuery() + } + + render() { + const builderObject = { + color: this.buildColor, + slider: this.buildSlider, + checkbox: this.buildCheckbox, + palette: this.buildPalette, + button: this.buildButton + } + + return + } +} diff --git a/src/components/base/settings/SettingsGeneral.tsx b/src/components/base/settings/SettingsGeneral.tsx new file mode 100644 index 0000000..c80ca11 --- /dev/null +++ b/src/components/base/settings/SettingsGeneral.tsx @@ -0,0 +1,153 @@ +import { Component, Vue } from 'vue-property-decorator' + +import AppSelector, { AppSelectorProps as SProps } from '@/components/app/AppSelector' +import AppCheckbox from '@/components/app/AppCheckbox' +import { PageModule } from '@/store/page' +import { LoginPosition } from '@/models/page' +import { AppModule } from '@/store/app' +import AppIcon from '@/components/app/AppIcon.vue' +import { generateDesktopIcons } from '@/utils/helper' +import AppButton from '@/components/app/AppButton' +import { osList } from '@/models/app' +import { LightdmUsers } from '@/models/lightdm' + +@Component({ components: { AppIcon, AppSelector } }) +export default class SettingsGeneral extends Vue { + get bodyClass() { + return AppModule.bodyClass + } + + get users() { + return AppModule.users + } + + get showFrameRate() { + return AppModule.showFrameRate + } + + get isViewThemeOnly() { + return AppModule.viewThemeOnly + } + + get languageList() { + return PageModule.languages.map((language) => ({ + text: language, + value: language + })) + } + + get menuPositionItems() { + const positions = ['top', 'left', 'right', 'bottom', 'center'] + return positions.map(word => ({ + value: word, + text: this.$t(`settings.login-position.${word}`).toString() + })) + } + + get menuPositionValue() { + return { + value: PageModule.loginPosition, + text: this.$t(`settings.login-position.${PageModule.loginPosition}`).toString() + } + } + + changeLanguage(value: string) { + this.$i18n.locale = value + localStorage.setItem('language', value) + PageModule.SET_STATE_PAGE({ key: 'language', value }) + } + + changeLoginPosition(position: string) { + localStorage.setItem('loginPosition', position) + PageModule.SET_STATE_PAGE({ key: 'loginPosition', value: position as LoginPosition }) + } + + changeDesktop(value: string) { + AppModule.SAVE_STATE_APP({ key: 'desktop', value }) + } + + changeOs(value: string) { + AppModule.SAVE_STATE_APP({ key: 'currentOs', value }) + } + + buildCheckbox(label: string, bodyClass: string) { + return AppModule.CHANGE_BODY_CLASS({ key: bodyClass, value }) } + /> + } + + buildSelector(labelI18n: SProps['label'], items: SProps['items'], value: SProps['value'], callback: (value: string) => void) { + const label = this.$t(`settings.${labelI18n}`) + return + } + + resetSettings() { + localStorage.clear() + AppModule.setUpSettings() + + const hasQyery = Object.keys(this.$route.query).length + if (hasQyery) { this.$router.replace({}) } + } + + buildUserBlock(user: LightdmUsers) { + const isActive = user.username === AppModule.username + const activateUser = () => AppModule.SAVE_STATE_APP({ key: 'username', value: user.username }) + + return
+ +

{ user.display_name }

+
+ } + + buildUsersBlock() { + return [ +

{ this.$t('settings.users') }

, +
{ this.users.map(this.buildUserBlock) }
+ ] + } + + buildSettingsSection() { + return
+

{ this.$t('settings.title') }

+ { this.buildSelector('choice-language', this.languageList, PageModule.language, this.changeLanguage) } + { this.buildSelector('login-position.about', this.menuPositionItems, this.menuPositionValue, this.changeLoginPosition) } + { !this.isViewThemeOnly && this.buildSelector('choice-desktop', generateDesktopIcons(), AppModule.currentDesktop?.key, this.changeDesktop) } + { !this.isViewThemeOnly && this.buildSelector('choice-os', osList, AppModule.currentOs, this.changeOs) } +
+ } + + buildCheckboxSection() { + return
+

{ this.$t('settings.performance') }

+ { this.buildCheckbox('Blur', 'blur') } + { this.buildCheckbox('Show framerate block', 'show-frame-rate') } + { this.buildCheckbox('Disable animation', 'no-transition') } + { this.buildCheckbox('Show only ui', 'only-graphic') } +
+ } + + render() { + return + } +} diff --git a/src/components/base/settings/SettingsThemes.tsx b/src/components/base/settings/SettingsThemes.tsx new file mode 100644 index 0000000..4d035c4 --- /dev/null +++ b/src/components/base/settings/SettingsThemes.tsx @@ -0,0 +1,42 @@ +import { Component, Vue } from 'vue-property-decorator' + +import { AppModule } from '@/store/app' + +@Component +export default class SettingsThemes extends Vue { + get themes() { + return AppModule.themes + } + + get activeTheme() { + return AppModule.activeTheme + } + + setImage(name: string) { + let url + + try { + url = require(`@/assets/images/themes/${name}/index.png`) + } catch { + url = 'notFound' + } + + return url + } + + render() { + return