This commit is contained in:
Ivan
2021-06-07 11:56:04 +08:00
commit c3c9fee2fb
1071 changed files with 195655 additions and 0 deletions

1
examples/extension/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

View File

@@ -0,0 +1,18 @@
import Vue from 'vue';
import App from './editor/index';
import Element from 'main/index.js';
import 'packages/theme-chalk/src/index.scss';
export default () => {
Vue.use(Element, { zIndex: 100000 });
const root = document.createElement('div');
document.body.appendChild(root);
window.ga = function() {
console.log(arguments);
};
new Vue({ // eslint-disable-line
render: h => h(App)
}).$mount(root);
};

View File

@@ -0,0 +1,6 @@
chrome.browserAction.onClicked.addListener(tab => {
chrome.tabs.executeScript(tab.id, {
file: 'entry.js'
});
})
;

View File

@@ -0,0 +1,212 @@
<template>
<div class="ext-panel" :class="{moving : moving}" :style="{top: `${top}px`, left: `${left}px`}" ref="editor" >
<img class="entrance touch-icon" src="./icon-entrance.png" v-show="!showSidebar" @click="toggleSidebar" />
<img class="close touch-icon" src="./icon-close.png" v-show="showSidebar" @click="toggleSidebar" />
<div class="editor" :style="{height: `${height}px`}" v-show="showSidebar">
<el-tabs v-model="activeTab" @tab-click="onTabClick">
<el-tab-pane label="Config" name="config">
<theme-configurator
:themeConfig="themeConfig"
:previewConfig="previewConfig"
:onUserConfigUpdate="onUserConfigUpdate"
from="extension"
></theme-configurator>
</el-tab-pane>
<el-tab-pane label="Gallery" name="gallery">
<gallery
ref='gallery'
:height="height"
:width="width - 7"
@action="onGalleryAction"
/>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script>
import ThemeConfigurator from '../../../components/theme-configurator';
import themeLoader from '../../../components/theme/loader';
import gallery from './gallery';
import { loadUserThemeFromLocal, saveUserThemeToLocal } from './utils';
import bus from '../../../bus.js';
import {
ACTION_APPLY_THEME
} from '../../../components/theme/constant.js';
let initX;
let initY;
let leftX = 0;
let topY = 25;
export default {
mixins: [themeLoader],
components: {
ThemeConfigurator,
gallery
},
data() {
return {
showSidebar: true,
previewConfig: {},
themeConfig: {},
top: topY,
left: leftX,
height: window.innerHeight - 30 * 2,
width: 0,
moving: false,
activeTab: 'config',
themeName: '',
userTheme: []
};
},
mounted() {
const editor = this.$refs.editor;
this.width = editor.offsetWidth;
leftX = window.innerWidth - 20 - this.width;
this.left = leftX;
editor.addEventListener('mousedown', e => {
initX = e.clientX;
initY = e.clientY;
leftX = this.left;
topY = this.top;
document.addEventListener('mousemove', this.moveFunc);
});
document.addEventListener('mouseup', e => {
document.removeEventListener('mousemove', this.moveFunc);
setTimeout(() => {this.moving = false;}, 0);
});
// chrome.storage.local.remove('ELEMENT_THEME_USER_CONFIG');
loadUserThemeFromLocal()
.then((result) => {
if (result) {
this.activeTab = 'gallery';
this.userTheme = result;
}
});
},
methods: {
checkLocalThemeConfig() {}, // disable mixin auto loading
toggleSidebar() {
if (this.moving) return;
this.showSidebar = !this.showSidebar;
if (!this.showSidebar) {
const windowWidth = window.innerWidth;
if (this.left + this.width * 0.5 < windowWidth * 0.5) {
this.left = 0;
} else {
this.left = windowWidth - 50;
}
} else {
this.moveEditor(this.left, this.top);
}
},
moveFunc(e) {
e.preventDefault();
const deltaX = initX - e.clientX;
const deltaY = initY - e.clientY;
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
this.moving = true;
}
this.moveEditor(leftX - deltaX, topY - deltaY);
},
moveEditor(x, y) {
const showSidebar = this.showSidebar;
let nextTop = y;
if (nextTop < 0) nextTop = 0;
const maxTop = window.innerHeight - (showSidebar ? (this.height + 5) : 50);
if (nextTop > maxTop) nextTop = maxTop;
this.top = nextTop;
let nextLeft = x;
if (nextLeft < 0) nextLeft = 0;
const maxLeft = window.innerWidth - (showSidebar ? (this.width + 5) : 50);
if (nextLeft > maxLeft) nextLeft = maxLeft;
this.left = nextLeft;
},
onGalleryAction(key, value) {
switch (key) {
case 'edit':
this.themeName = value.name;
this.themeConfig = JSON.parse(value.theme);
bus.$emit(ACTION_APPLY_THEME, this.themeConfig);
this.activeTab = 'config';
break;
default:
return;
}
},
onTabClick(e) {
if (e && e.name === 'gallery') {
this.$refs.gallery.init();
}
},
onUserConfigUpdate(userConfig) {
if (this.themeName) {
this.userTheme.forEach((config) => {
if (config.name === this.themeName) {
config.update = Date.now();
config.theme = JSON.stringify(userConfig);
}
});
} else {
this.themeName = `Theme-${Date.now()}`;
this.userTheme.push({
update: Date.now(),
name: this.themeName,
theme: JSON.stringify(userConfig)
});
}
saveUserThemeToLocal(this.userTheme);
}
}
};
</script>
<style scoped>
.ext-panel {
position: fixed;
background: transparent;
user-select: none;
z-index: 100000;
}
.ext-panel.moving{
cursor: move;
}
.ext-panel.moving .touch-icon{
cursor: move;
}
.ext-panel .touch-icon{
cursor: pointer;
}
.ext-panel .close {
position: absolute;
right: 0;
top: 0;
height: 20px;
width: 20px;
z-index: 100001;
}
.ext-panel .entrance {
height: 50px;
width: 50px;
}
.ext-panel .editor {
overflow: hidden;
background: #f5f7fa;
border: 1px solid #ebeef5;
border-radius: 5px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
margin: 5px 5px 0 0;
min-width: 261px;
}
</style>
<style>
.ext-panel .editor .el-tabs__content, .ext-panel .editor .el-tabs--top, .ext-panel .editor .el-tab-pane {
height: 100%;
}
.ext-panel .el-tabs__nav-scroll >div {
transform: translateX(60px)!important;
}
.ext-panel .editor-main {
padding: 0 18px 70px;
}
</style>

View File

@@ -0,0 +1,185 @@
<template>
<div :style="{
height: `${height}px`,
width: `${width}px`
}"
class="main"
>
<ul
class="theme-card-list"
>
<li class="theme-card" v-for="item in userTheme" :key="item.name">
<theme-card type="user" :config="item" @action="onAction" from="extension"></theme-card>
</li>
<li class="theme-card">
<theme-card type="upload" :config="{name: 'upload'}" @action="onAction"></theme-card>
</li>
</ul>
<el-dialog :visible.sync="copyDialogVisible" :modal-append-to-body="false">
<el-form :model="copyForm" ref="copyForm" :rules="copyFormRule">
<el-form-item label="主题名称" prop="name">
<el-input v-model="copyForm.name"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="closeCopyForm">取消</el-button>
<el-button type="primary" @click="copyToUser">确认</el-button>
</div>
</el-dialog>
</div>
</template>
<style scoped>
ul, li {
padding: 0;
margin: 0;
}
.main {
overflow: auto;
}
.theme-card-list {
padding-bottom: 50px;
width: 90%;
margin: 0 auto;
}
.theme-card {
display: inline-block;
height: 140px;
height: 15vw;
width: 100%;
max-height: 230px;
flex: 0 0 24%;
cursor: default;
vertical-align: bottom;
}
</style>
<style>
.theme-card .theme-card-item {
margin-top: 0;
}
.theme-card .theme-card-item.is-upload {
height: 80%
}
</style>
<script>
import ThemeCard from '../../../components/theme/theme-card.vue';
import { loadUserThemeFromLocal, saveUserThemeToLocal } from './utils';
export default {
props: {
height: Number,
width: Number
},
data() {
return {
userTheme: [],
copyDialogVisible: false,
copyForm: {},
copyFormRule: {
name: [{
validator: this.validateCopyName,
trigger: 'blur'
}]
}
};
},
components: {
ThemeCard
},
mounted() {
this.init();
},
methods: {
init() {
loadUserThemeFromLocal().then(result => {
if (!result) return;
this.userTheme = result;
});
},
onAction(key, value) {
switch (key) {
case 'copy':
this.openCopyForm(value.theme);
break;
case 'upload':
this.openCopyForm(value);
break;
case 'delete':
this.$confirm('确定要删除这个主题?', '提示', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deleteUserThemeByName(value.name);
}).catch(() => {});
break;
case 'rename':
this.openRenameForm(value.name);
break;
default:
this.$emit('action', key, value);
return;
}
},
openCopyForm(theme) {
this.copyForm.theme = theme;
this.copyDialogVisible = true;
},
openRenameForm(name) {
this.copyForm.oldname = name;
this.copyDialogVisible = true;
},
closeCopyForm() {
this.copyDialogVisible = false;
this.$nextTick(() => {
this.copyForm = {};
});
},
validateCopyName(rule, value, callback) {
if (!value) {
callback(new Error('主题名称是必填项'));
} else if (this.filterUserThemeByName(value).length > 0) {
callback(new Error('主题名称重复'));
} else {
callback();
}
},
copyToUser() {
this.$refs.copyForm.validate((valid) => {
if (valid) {
const { theme, name, oldname } = this.copyForm;
if (theme) {
// copy
this.userTheme.push({
update: Date.now(),
name,
theme
});
} else {
// rename
this.userTheme.forEach((config) => {
if (config.name === oldname) {
config.update = Date.now();
config.name = name;
}
});
}
this.saveToLocal();
this.closeCopyForm();
}
});
},
filterUserThemeByName(name, include = true) {
return this.userTheme.filter((theme) => (include ? theme.name === name : theme.name !== name));
},
saveToLocal() {
saveUserThemeToLocal(this.userTheme);
},
deleteUserThemeByName(name) {
this.userTheme = this.filterUserThemeByName(name, false);
this.saveToLocal();
}
}
};
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -0,0 +1,13 @@
<template>
<editor />
</template>
<script>
import editor from './editor';
export default {
components: {
editor
}
};
</script>

View File

@@ -0,0 +1,18 @@
const ELEMENT_THEME_USER_CONFIG = 'ELEMENT_THEME_USER_CONFIG';
export const loadFromLocal = (key) => {
return new window.Promise((resolve) => {
chrome.storage.local.get([key], (result) => {
resolve(result[key]);
});
});
};
export const saveToLocal = (key, value) => {
chrome.storage.local.set({[key]: value});
};
export const loadUserThemeFromLocal = () => {
return loadFromLocal(ELEMENT_THEME_USER_CONFIG);
};
export const saveUserThemeToLocal = (value) => {
saveToLocal(ELEMENT_THEME_USER_CONFIG, value);
};

View File

@@ -0,0 +1,7 @@
import init from './app';
if (!window.ElementThemeRollerInit) {
window.ElementThemeRollerInit = true;
init();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,27 @@
{
"name": "Element Theme Roller",
"version": "0.0.9",
"description": "Customize all Design Tokens of Element UI and preview in real-time",
"web_accessible_resources": ["entry.js"],
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"icons": {
"128": "icon.png"
},
"browser_action": {
"default_icon": "icon.png",
"default_title": "Element Theme Roller"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"manifest_version": 2,
"permissions": [
"activeTab",
"storage",
"https://*.ele.me/",
"https://*.elenet.me/"
]
}