1
0
mirror of https://github.com/sahadev/vue-component-creater-ui.git synced 2025-09-28 07:13:21 +08:00
This commit is contained in:
shangbin
2021-04-09 20:06:44 +08:00
commit e87ebafdcb
97 changed files with 33753 additions and 0 deletions

View File

@@ -0,0 +1,321 @@
<template>
<el-card class="attribute-container">
<center>
<el-switch v-model="editMode" active-text="自由编辑" inactive-text="约束编辑" active-color="#13ce66"
inactive-color="#13ce66">
</el-switch>
</center>
<div style="margin-top: 20px;">
<div name="1" v-show="!editMode">
<div>
<div class="item" v-for="(item, index) in localAttributes" :key="index">
<el-input v-model="item.key" :placeholder="'key' + index" class="half-width"></el-input>
<div class="split">:</div>
<el-input v-model="item.value" :placeholder="'value' + index" class="half-width"></el-input>
<i class="el-icon-minus" @click="deleteItem(index)"></i>
</div>
<div class="quick-add-root">
快速增加一些属性:
<div style="margin-top: 5px;">
<transition name="el-zoom-in-center">
<el-tag v-if="attributeKeys.indexOf('class') == -1" size="mini" type="success" @click="onClassClick"
effect="dark">Class
</el-tag>
</transition>
<transition name="el-zoom-in-center">
<el-tag v-if="attributeKeys.indexOf('@click') == -1" size="mini" type="success" @click="onEventClick"
effect="dark">点击事件</el-tag>
</transition>
<transition name="el-zoom-in-center">
<el-tag v-if="!attributeKeys.includes('__text__')" size="mini" type="success" @click="onTextClick"
effect="dark">文本内容</el-tag>
</transition>
</div>
</div>
</div>
</div>
<div name="2" v-show="editMode">
<el-input type="textarea" :autosize="{ minRows: 4}" placeholder="请输入属性, 以key: value的形式(冒号后要有空格)"
v-model="textAttributes">
</el-input>
</div>
</div>
<center style="margin-top: 10px;">
<el-tooltip class="item" effect="dark" content="新增属性 ctrl+a" placement="bottom">
<el-button type="primary" class="center" @click="createNew" icon="el-icon-circle-plus-outline" circle>
</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="保存属性 ctrl+s" placement="bottom">
<el-button type="success" class="center" @click="save" icon="el-icon-refresh" circle></el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="移除该组件 ctrl+d" placement="bottom">
<el-button v-if="enableRemoveButton" type="danger" class="center" icon="el-icon-delete" @click="remove" circle>
</el-button>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="复制一个兄弟组件 ctrl+c" placement="bottom">
<el-button v-if="enableBroButton" type="primary" class="center" icon="el-icon-copy-document" @click="copyBro"
circle>
</el-button>
</el-tooltip>
<center>
<span class="shortcut-tip">支持快捷键操作</span>
</center>
</center>
</el-card>
</template>
<script>
import { getRawComponentKey, getRawComponentContent } from "@/utils/common";
import { brotherEleEnum, copyBroCode } from "@/libs/bro-ele-config";
const keymaster = require('keymaster');
export default {
props: ['__rawVueInfo__', 'enableRemoveButton', 'shortcutInitMode'],// __rawVueInfo__为当前编辑的原始代码对象, shortcutInitMode快捷键的初始化方式
data: function () {
return {
input: "",
localAttributes: [],
enable: true,
autoplay: false,
editMode: false,
textAttributes: ''
};
},
mounted() {
const container = document.querySelector(".attribute-container");
container.addEventListener("click", (event) => {
event.stopPropagation();
});
if (this.shortcutInitMode === 'auto') {
// 这种方式用于在检视图中,因为它依赖组件的创建和销毁
this.initShortcut();
}
},
beforeDestroy() {
if (this.shortcutInitMode === 'auto') {
// 防止内存泄漏
this.destroyShortcut();
}
},
methods: {
destroyShortcut() {
console.log(`destroyShortcut by mode: ${this.shortcutInitMode}`)
keymaster.unbind('⌘+a, ctrl+a');
keymaster.unbind('⌘+s, ctrl+s');
keymaster.unbind('⌘+d, ctrl+d');
keymaster.unbind('⌘+c, ctrl+c');
},
initShortcut() {
console.log(`init by mode: ${this.shortcutInitMode}`)
keymaster('⌘+a, ctrl+a', () => {
if (this.enable) {
this.createNew();
return false
}
});
keymaster('⌘+s, ctrl+s', () => {
if (this.enable) {
this.save();
return false
}
});
keymaster('⌘+d, ctrl+d', () => {
if (this.enable) {
this.remove();
return false
}
});
keymaster('⌘+c, ctrl+c', () => {
if (this.enable && this.enableBroButton) {
this.copyBro();
return false
}
});
},
onClassClick() {
this.localAttributes.push({ key: "class", value: "class-name" });
},
onEventClick() {
this.localAttributes.push({ key: "@click", value: "onEventClick" });
},
onTextClick() {
this.localAttributes.push({ key: "__text__", value: "content" });
},
createNew() {
window.trackManager.track("lc_on_attribute_add");
this.localAttributes.push({ key: "", value: "" });
},
save() {
window.trackManager.track("lc_on_attribute_save");
try {
let resultList = [];
if (!this.editMode) {
resultList = this.localAttributes.filter((item) => {
return !!item.key;
});
} else {
const attributes = this.textAttributes.split('\n');
resultList = attributes.map(item => {
const [key, value] = item.split(": ");
return {
key, value
}
})
this.localAttributes = resultList;
}
this.$emit("save", { resultList, lc_id: this.rawInfoID });
this.$notify({
title: "提示",
message: '代码已更新',
position: 'bottom-right',
type: 'success'
});
} catch (error) {
this.$message.error(error);
}
},
remove() {
window.trackManager.track("lc_on_attribute_remove");
this.$emit("remove", { lc_id: this.rawInfoID });
},
deleteItem(index) {
window.trackManager.track("lc_on_element_delete");
this.localAttributes.splice(index, 1);
},
copyBro() {
copyBroCode(this.__rawVueInfo__);
this.$emit('codeRefresh');
},
onShow() {
// 这种方式适用于常规模式下的初始化,因为这个实例初始化后不会被销毁,一直常驻内存。但又不能受到其它实例销毁时的影响,所以需要明确的再次初始化。
this.initShortcut();
},
onHide() {
this.destroyShortcut();
},
},
computed: {
componentName() {
return this.__rawVueInfo__ ? getRawComponentKey(this.__rawVueInfo__) : '';
},
rawInfoID() {
return this.__rawVueInfo__ ? getRawComponentContent(this.__rawVueInfo__).lc_id : '';
},
enableBroButton() {
const checkResult = brotherEleEnum().find(item => {
return item.name == this.componentName;
});
return checkResult && checkResult.length != 0;
},
attributeList() {
const result = [];
const vueRawInfo = this.__rawVueInfo__;
if (vueRawInfo) {
const object = vueRawInfo[getRawComponentKey(vueRawInfo)];
for (const key in object) {
if (object.hasOwnProperty(key)) {
const element = object[key];
if (typeof element !== "object" && key != 'lc-mark' && key != 'lc_id') { // 这两个是保留字段,不对外提供使用
result.push({ key: key, value: element });
}
}
}
}
return result;
},
attributeKeys() {
return this.localAttributes.map(item => item.key)
}
},
watch: {
attributeList: {
handler: function () {
this.localAttributes = this.attributeList;
},
immediate: true
},
localAttributes(newValue) {
if (newValue.length === 0) {
newValue.push({ key: "", value: "" });
}
this.textAttributes = newValue.map(item => `${item.key}: ${item.value}`).join('\n')
}
},
};
</script>
<style scoped>
.container {
padding: 10px;
width: 50%;
text-align: center;
}
.half-width {
width: 0%;
flex-grow: 1;
}
.center {
display: inline-block !important;
margin: 10px 10px;
}
.item {
display: flex;
margin-bottom: 10px;
align-items: center;
}
.quick-add-root {
padding: 5px 10px;
border: 1px dashed #c6c6c6;
border-radius: 5px;
font-size: 12px;
color: gray;
}
.split {
width: 30px;
text-align: center;
font-weight: bold;
}
.el-icon-plus,
.el-icon-minus {
margin-left: 10px;
}
.el-button + .el-button {
margin-left: 0px;
}
.shortcut-tip {
font-size: 12px;
color: grey;
padding: 2px;
border-bottom: grey solid 1px;
}
</style>

120
src/components/Code.vue Normal file
View File

@@ -0,0 +1,120 @@
<template>
<el-dialog title="代码预览" :visible.sync="codeDialogVisible" width="70%" top="10vh" :before-close="handleClose"
:center=true>
<pre style="max-height: 60vh;">
<code v-html="formatCode"></code>
</pre>
<div>
<center style="color: #666; font-size: 12px;">使用代码前请确认相应的组件库已集成至项目</center>
</div>
<span slot="footer">
<el-tooltip effect="dark" content="拷贝" placement="left">
<img class="round-icon" :src="iconCopy" alt="" @click="copyCheck">
</el-tooltip>
<el-tooltip effect="dark" content="下载" placement="right">
<img class="round-icon" :src="iconDownload" alt="" @click="download">
</el-tooltip>
</span>
</el-dialog>
</template>
<script>
import './prism.css'
import Prism from "prismjs";
import prettier from "prettier/standalone";
import parserHtml from "prettier/parser-html";
import copy from 'copy-to-clipboard';
import { saveAs } from "file-saver";
export default {
props: ['rawCode', 'codeDialogVisible'],
data() {
return {
// 在此自动生成
iconCopy: require("@/assets/icon/copy-outline.svg"),
iconDownload: require("@/assets/icon/code-download-outline.svg"),
};
},
beforeCreate() { },
created() { },
beforeMount() { },
mounted() { },
beforeUpdate() { },
updated() { },
destoryed() { },
methods: {
// 在此自动生成
request() {
// 网络请求,可选
},
handleClose() {
this.$emit("update:codeDialogVisible", false);
},
copyCheck() {
this.copy();
},
copy() {
if (copy(this.prettyCode)) {
this.$message.success("代码已复制到剪贴板");
} else {
this.$message.error("代码复制有点问题?");
}
},
download() {
window.trackManager.track("lc_on_code_download");
let blob = new Blob([this.prettyCode], {
type: "text/plain;charset=utf-8",
});
saveAs(blob, "VueComponent.vue");
},
},
watch: {
codeDialogVisible(newValue) {
if (newValue) {
} else {
}
}
},
computed: {
prettyCode() {
return prettier.format(this.rawCode, {
parser: "html",
plugins: [parserHtml],
vueIndentScriptAndStyle: true,
});
},
formatCode() {
// 代码格式化工具需要赓续为支持Vue的当前prettier对js代码不够友好
return Prism.highlight(this.prettyCode, Prism.languages.markup, "html");
}
},
fillter: {},
};
</script>
<style scoped>
/* 在此自动生成 */
::v-deep .el-dialog__body {
padding: 0 30px !important;
}
.round-icon {
background: #4dba87;
width: 40px;
height: 40px;
border-radius: 20px;
padding: 10px;
margin-left: 10px;
box-sizing: border-box;
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<preview :value="preview" class="panel"></preview>
</template>
<script>
import Preview from '@/components/Preview';
import { parseComponent } from 'vue-template-compiler/browser';
import getImports from '@/utils/get-imports';
import getPkgs from '@/utils/get-pkgs';
import isAbsouteUrl from 'is-absolute-url';
import * as params from '@/utils/params';
export default {
props: ['code'],
components: {
Preview
},
data: () => ({
preview: '',
}),
watch: {
code: {
handler: function (newValue) {
this.compile(newValue);
},
immediate: true
}
},
methods: {
async compile(code) {
if (!code) {
return;
}
const imports = [];
const { template, script, styles, customBlocks } = parseComponent(code);
let config;
if ((config = customBlocks.find(n => n.type === 'config'))) {
params.clear();
params.parse(config.content);
}
let compiled;
const pkgs = [];
let scriptContent = 'exports = { default: {} }';
if (script) {
try {
compiled = window.Babel.transform(script.content, {
presets: ['es2015', 'es2016', 'es2017', 'stage-0'],
plugins: [[getImports, { imports }]]
}).code;
} catch (e) {
this.preview = `<pre style="color: red">${e.message}</pre>`;
return;
}
scriptContent = await getPkgs(compiled, imports, pkgs);
}
const heads = this.genHeads();
const scripts = [];
pkgs.forEach(pkg => {
scripts.push(
`<script src=//packd.now.sh/${pkg.module}${pkg.path}?name=${pkg.name
}><\/script>`
);
});
styles.forEach(style => {
heads.push(`<style>${style.content}</style>`);
});
scripts.push(`
<script>
var exports = {};
${scriptContent}
var component = exports.default;
component.template = component.template || ${JSON.stringify(
template.content
)}
new Vue(component).$mount('#app')
<\/script>`);
this.preview = {
head: heads.join('\n'),
body: '<div lc_id="app"></div>' + scripts.join('\n')
};
},
genHeads() {
let heads = [];
const { pkgs, css } = params.get();
return [].concat(
[]
.concat(pkgs)
.map(
pkg =>
`<script src="${isAbsouteUrl(pkg) ? '' : prefix}${pkg}"><\/script>`
),
css.map(
item =>
`<link rel=stylesheet href="${isAbsouteUrl(item) ? '' : prefix
}${item}">`
)
);
},
}
};
</script>
<style src="modern-normalize"></style>
<style scoped>
.panel {
background-color: white;
}
</style>

View File

@@ -0,0 +1,160 @@
<template>
<el-drawer :visible.sync="drawer" :with-header="false" size="70%" direction="btt">
<div class="container">
<center>组件结构检视图
<br>
<span style="font-size:12px;">Components
Structure</span>
</center>
<el-row :gutter="20" style="height:0px;flex-grow:1;">
<el-col :span="16" style="height: 100%;">
<div style="overflow: scroll;height:100%; margin: 0 20px;padding: 10px;">
<v-tree :radio="true" :canDeleteRoot="false" :data='treeData' :draggable='false' :halfcheck='false'
:multiple="false" @node-click="onNodeClick" />
</div>
</el-col>
<el-col :span="8">
<attribute-input ref="attributeInput" :enableRemoveButton="true" v-if="currentEditRawInfo && drawer"
@save="onSaveAttr" shortcutInitMode="auto" @remove="onRemove" @codeRefresh="codeRefresh"
:__rawVueInfo__="currentEditRawInfo">
</attribute-input>
</el-col>
</el-row>
</div>
</el-drawer>
</template>
<script>
import "./halower-tree.min.css";
import { VTree } from '@/libs/v2-tree'
import { isObject } from "@/utils/common";
export default {
props: ['visible'],
components: {
VTree,
AttributeInput: resolve => { require(["./AttributeInput"], resolve) },
},
data() {
return {
// 在此自动生成
treeData: [],
currentEditRawInfo: null
};
},
beforeCreate() { },
created() { },
beforeMount() { },
mounted() { },
beforeUpdate() { },
updated() { },
destoryed() { },
methods: {
// 在此自动生成
request() {
// 网络请求,可选
},
codeRefresh() {
this.$emit('codeRefresh');
},
// 获取这个节点的key
getRawComponentKey(__rawVueInfo__) {
return Object.keys(__rawVueInfo__)[0];
},
convertStructure(rawInfo) {
const title = this.getRawComponentKey(rawInfo);
const object = rawInfo[title];
const children = [];
if (isObject(object)) {
for (const key in object) {
if (object.hasOwnProperty(key)) {
if (key === '__children') {
const element = object[key];
element.forEach(item => {
const temp = this.convertStructure(item);
temp && children.push(temp);
})
} else if (isObject(object[key])) {
// 组成一个新的结构,适配只有一个子节点的数据结构
const __obj = {};
__obj[key] = object[key];
const child = this.convertStructure(__obj);
child && children.push(child);
}
}
}
return {
title: title,
expanded: true,
children: children,
rawInfo: rawInfo,
}
} else {
return null;
}
},
onNodeClick(nodeInfo) {
this.currentEditRawInfo = nodeInfo.rawInfo;
},
onRemove({ lc_id }) {
this.$emit("remove", { lc_id });
// 为了降低复杂性,这里先不做删除失败的处理
this.currentEditRawInfo = null;
},
onSaveAttr(resultList) {
this.$emit("save", resultList);
},
updateCode(codeRawInfo) {
this.treeData = [this.convertStructure(codeRawInfo)];
},
},
watch: {
canInitShortcut(newValue) {
}
},
computed: {
drawer: {
get() {
return this.visible;
},
set() {
this.$emit('update:visible', false);
}
},
canInitShortcut() {
return this.currentEditRawInfo !== null && this.drawer;
}
},
fillter: {},
};
</script>
<style scoped>
/* 在此自动生成 */
center {
padding: 20px;
}
::v-deep .el-drawer__body {
height: 100%;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -0,0 +1,43 @@
<template>
<div ref="container" id="container">
<div ref="iframe"></div>
</div>
</template>
<style scoped>
#container {
height: 100%;
width: 100%;
}
</style>
<script>
import createIframe from '@/utils/iframe';
const sandboxAttributes = [
'allow-modals',
'allow-forms',
'allow-pointer-lock',
'allow-popups',
'allow-same-origin',
'allow-scripts'
];
export default {
props: ['value'],
mounted() {
this.iframe = createIframe({
container: this.$refs.container,
el: this.$refs.iframe,
sandboxAttributes
});
},
watch: {
value(val) {
this.iframe.setHTML(val);
}
}
};
</script>

View File

@@ -0,0 +1,298 @@
<template lc_id="OpUzJauqXb">
<div lc_id="wL/ZpJzwjh" class="container">
<nav style="display:flex;">
<div :index="index + ''" v-for="(item, index) in iconArray" @click="onSelectElement(index)" :key="item.icon"
:class="{'active':currentIndex === index}" class="main-icon-container">
<img v-if="item.enable" :src="item.icon" class="icon">
<el-tooltip v-else class="item" effect="dark" content="暂未开放,敬请期待" placement="right">
<img :src="item.icon" class="icon" style="width: 34px;height: 34px;">
</el-tooltip>
</div>
<div class="bottom-toolbar">
<div class="main-title">
Low Code Generator
</div>
<div>
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
<i ref="help" class="el-icon-question" style="font-size:22px;color:#4dba87;"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-circle-check">基础组件数: {{ componentUnitNum }}
</el-dropdown-item>
<el-dropdown-item icon="el-icon-document" command="help">说明文档</el-dropdown-item>
<el-dropdown-item icon="el-icon-chat-line-round" command="chat">在线沟通</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</nav>
<nav v-if="currentSelectBrand.titleArray && currentSelectBrand.titleArray.length > 0">
<center style="margin-bottom:10px;">
<div style="padding:5px;font-size:12px;color:grey;">快速查找需要的</div>
<el-autocomplete class="inline-input" v-model="componentSearch" :fetch-suggestions="querySearch" size="mini"
placeholder="请输入..." @select="handleSelect"></el-autocomplete>
</center>
<div class="dismiss-scroll">
<div v-for="(item, index) in currentSelectBrand.titleArray" :key="item.title" class="second-nav"
:class="{'active':currentSelectBrand.selectIndex === index}" @click="selectSubnav(currentSelectBrand, index)">
<div style="weight: 600; font-size: 16px;">{{item.titleChinese}}</div>
<div style="font-size: 12px; color: grey;">{{item.titleEnglish}}</div>
</div>
</div>
</nav>
<div style="overflow:scroll;padding:0 10px;">
<keep-alive>
<component :is="currentSelectBrand.componentName" @mounted='onMouted'></component>
</keep-alive>
</div>
</div>
</template>
<script>
// import vant from "../rawComponents/vant";
// import iview from "../rawComponents/iview";
// import quasar from "../rawComponents/quasar";
const { generateColor } = require('random-color-generator2');
export default {
data() {
return {
activeName: "0",
componentSearch: '',
iconArray: [{
icon: require('@/assets/logo/html-n.png'),
clickCallback: this.onSelectElement,
className: "demonstration-raw",
enable: true,
componentName: 'raw',
titleArray: [],
}, {
icon: require('@/assets/logo/element-n.png'),
clickCallback: this.onSelectElement,
className: "demonstration-element",
selectIndex: 0,
componentName: 'ele',
enable: true,
titleArray: [],
}, {
icon: require('@/assets/logo/vant-n.png'),
enable: false
}, {
icon: require('@/assets/logo/iview-n.png'),
enable: false
}, {
icon: require('@/assets/logo/quasar-n.png'),
enable: false
},],
currentIndex: 1
};
},
methods: {
querySearch(queryString, cb) {
const result = queryString ? this.currentSelectBrand.titleArray.filter(item => {
return item.titleChinese.indexOf(queryString) >= 0 || (item.titleEnglish && item.titleEnglish.toLowerCase().indexOf(queryString.toLowerCase()) >= 0)
}) : this.currentSelectBrand.titleArray;
cb(result.map(item => {
return {
value: item.titleChinese + ' ' + item.titleEnglish,
element: item.element
}
}));
},
scrollTo(item) {
item.element.scrollIntoView({
behavior: "smooth",
block: "start"
});
},
handleSelect(item) {
this.scrollTo(item);
},
handleCommand(command) {
if (command === 'help') {
window.open('/doc')
} else if (command === 'chat') {
window.open('https://gitter.im/low_code_generator/community?utm_source=share-link&utm_medium=link&utm_campaign=share-link')
}
},
onSelectElement(index) {
if (this.iconArray[index].enable) {
this.currentIndex = index;
}
},
onMouted() {
// 这里目前只支持ele所以只写了1
this.initOnly(this.iconArray[1]);
},
selectSubnav(obj, index) {
obj.selectIndex = index;
this.scrollTo(obj.titleArray[index]);
},
init() {
this.initOnly(this.iconArray[0]);
},
initOnly(mountedObject) {
const titles = document.getElementsByClassName(mountedObject.className);
if (titles.length > 1) {
for (let i = 0; i < titles.length; i++) {
const element = titles[i];
const arr = element.textContent.split(' ');
mountedObject.titleArray.push({
titleChinese: arr.length === 2 ? arr[1] : arr[0],
titleEnglish: arr.length === 1 ? null : arr[0],
element: element
})
}
} else if (titles.length === 1) {
mountedObject.onlyTitle = {
element: titles[0]
}
}
},
surprise() {
const that = this;
function color() {
that.$refs.help.style = `color:${that.colorPointer.next().value};font-size: 24px;`;
window.requestAnimationFrame(color);
}
window.requestAnimationFrame(color);
}
},
created() { this.colorPointer = generateColor(true, 2); },
mounted() {
this.init();
// this.surprise();
},
computed: {
currentSelectBrand() {
return this.iconArray[this.currentIndex];
},
componentUnitNum() {
return 0;
}
},
watch: {
currentIndex() {
// 对没有二级菜单的选项来说
if (this.currentSelectBrand.onlyTitle) {
this.scrollTo(this.currentSelectBrand.onlyTitle);
}
}
},
components: {
raw: () => import("../rawComponents/raw"),
// vant,
// iview,
// quasar,
ele: () => import("../rawComponents/element"),
},
};
</script>
<style scoped lang="scss">
.container {
display: flex;
height: 100%;
}
nav {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
border-right: 1px solid #f0f0f0;
}
.second-nav {
padding: 10px 15px;
width: 130px;
&:hover {
background-color: #ecf5ff;
border-radius: 5px;
color: #409eff;
}
}
.icon {
width: 34px;
height: 34px;
border-radius: 5px;
}
.main-icon-container {
padding: 10px;
line-height: 0;
&:hover {
background: rgb(236, 245, 255);
border-radius: 5px;
}
}
.active {
border-right: 3px solid rgb(19, 206, 102);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}
::v-deep .el-submenu__title {
padding: 0 15px !important;
}
.dismiss-scroll {
overflow: scroll;
}
::-webkit-scrollbar {
/*隐藏滚轮*/
display: none;
}
.main-title {
font-size: 32px;
font-weight: 700;
color: #4dba87;
transform: rotate(-90deg);
white-space: nowrap;
position: absolute;
bottom: 11px;
left: 0;
transform-origin: -2% 10%;
}
.subtitle {
font-size: 14px;
font-weight: 700;
margin-left: 20px;
}
.bottom-toolbar {
flex-grow: 1;
position: relative;
display: flex;
flex-direction: column-reverse;
padding: 20px 0;
}
</style>

2
src/components/halower-tree.min.css vendored Normal file

File diff suppressed because one or more lines are too long

125
src/components/prism.css Normal file
View File

@@ -0,0 +1,125 @@
/* PrismJS 1.20.0
https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clike+javascript */
/**
* okaidia theme for JavaScript, CSS and HTML
* Loosely based on Monokai textmate theme by http://www.monokai.nl/
* @author ocodia
*/
code,
pre {
color: #f8f8f2;
background: none;
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
/* Code blocks */
pre {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code,
pre {
background: #272822;
}
/* Inline code */
:not(pre) > code {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #f8f8f2;
}
.token.namespace {
opacity: 0.7;
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: #f92672;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: #f8f8f2;
}
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
color: #e6db74;
}
.token.keyword {
color: #66d9ef;
}
.token.regex,
.token.important {
color: #fd971f;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}