add submissions.

This commit is contained in:
2023-01-15 17:12:07 +08:00
parent a62f744a9a
commit eaa5a8516e
34 changed files with 1652 additions and 1592 deletions

View File

@@ -1,98 +1,98 @@
<script setup lang="ts">
import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types"
import { isMobile } from "~/shared/composables/breakpoints"
import { isDark } from "../composables/dark"
interface Props {
value: string
language?: LANGUAGE
height?: string
fontSize?: number
class?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
height: "calc(100vh - 100px)",
fontSize: 20,
class: "",
})
const emit = defineEmits<{
(e: "change", value: string): void
}>()
const monacoEditorRef = ref()
let editor: Monaco.editor.IStandaloneCodeEditor
onMounted(function () {
const model = window.monaco.editor.createModel(
props.value,
LANGUAGE_VALUE[props.language]
)
editor = window.monaco.editor.create(monacoEditorRef.value, {
model,
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: {
enabled: false,
},
lineNumbersMinChars: 3,
automaticLayout: true, // 自适应布局
tabSize: 4,
fontSize: isMobile.value ? 20 : 24, // 字体大小
scrollBeyondLastLine: false,
lineDecorationsWidth: 0,
scrollBeyondLastColumn: 0,
glyphMargin: false,
scrollbar: {
useShadows: false,
vertical: "hidden",
horizontal: "hidden",
},
overviewRulerLanes: 0,
})
model.onDidChangeContent(() => {
const value = model.getValue().toString()
emit("change", value)
})
editor.onKeyDown((e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
e.preventDefault()
}
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
e.preventDefault()
}
})
watchEffect(() => {
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
})
watchEffect(() => {
if (props.value !== model.getValue()) {
model.setValue(props.value)
}
})
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
})
onUnmounted(() => {
editor && editor.dispose()
})
</script>
<template>
<div
ref="monacoEditorRef"
:class="props.class"
:style="{ height: props.height }"
></div>
</template>
<style scoped></style>
<script setup lang="ts">
import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types"
import { isMobile } from "~/shared/composables/breakpoints"
import { isDark } from "../composables/dark"
interface Props {
value: string
language?: LANGUAGE
height?: string
fontSize?: number
class?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
height: "calc(100vh - 100px)",
fontSize: 20,
class: "",
})
const emit = defineEmits<{
(e: "change", value: string): void
}>()
const monacoEditorRef = ref()
let editor: Monaco.editor.IStandaloneCodeEditor
onMounted(function () {
const model = window.monaco.editor.createModel(
props.value,
LANGUAGE_VALUE[props.language]
)
editor = window.monaco.editor.create(monacoEditorRef.value, {
model,
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: {
enabled: false,
},
lineNumbersMinChars: 3,
automaticLayout: true, // 自适应布局
tabSize: 4,
fontSize: isMobile.value ? 20 : 24, // 字体大小
scrollBeyondLastLine: false,
lineDecorationsWidth: 0,
scrollBeyondLastColumn: 0,
glyphMargin: false,
scrollbar: {
useShadows: false,
vertical: "hidden",
horizontal: "hidden",
},
overviewRulerLanes: 0,
})
model.onDidChangeContent(() => {
const value = model.getValue().toString()
emit("change", value)
})
editor.onKeyDown((e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
e.preventDefault()
}
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
e.preventDefault()
}
})
watchEffect(() => {
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
})
watchEffect(() => {
if (props.value !== model.getValue()) {
model.setValue(props.value)
}
})
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
})
onUnmounted(() => {
editor && editor.dispose()
})
</script>
<template>
<div
ref="monacoEditorRef"
:class="props.class"
:style="{ height: props.height }"
></div>
</template>
<style scoped></style>

View File

@@ -1,42 +1,42 @@
<script setup lang="ts">
import { isDesktop } from "~/shared/composables/breakpoints"
interface Props {
total: number
limit: number
page: number
}
const props = withDefaults(defineProps<Props>(), {
limit: 10,
page: 1,
})
const emit = defineEmits(["update:limit", "update:page"])
const limit = ref(props.limit)
const page = ref(props.page)
watch(limit, () => emit("update:limit", limit))
watch(page, () => emit("update:page", page))
</script>
<template>
<el-pagination
class="right margin"
:layout="isDesktop ? 'prev,pager,next,sizes' : 'prev,next,sizes'"
background
:total="props.total"
:page-sizes="[10, 20, 30]"
:pager-count="5"
v-model:page-size="limit"
v-model:current-page="page"
/>
</template>
<style scoped>
.margin {
margin-top: 24px;
}
.right {
float: right;
}
</style>
<script setup lang="ts">
import { isDesktop } from "~/shared/composables/breakpoints"
interface Props {
total: number
limit: number
page: number
}
const props = withDefaults(defineProps<Props>(), {
limit: 10,
page: 1,
})
const emit = defineEmits(["update:limit", "update:page"])
const limit = ref(props.limit)
const page = ref(props.page)
watch(limit, () => emit("update:limit", limit))
watch(page, () => emit("update:page", page))
</script>
<template>
<el-pagination
class="right margin"
:layout="isDesktop ? 'prev,pager,next,sizes' : 'prev,next,sizes'"
background
:total="props.total"
:page-sizes="[10, 20, 30]"
:pager-count="5"
v-model:page-size="limit"
v-model:current-page="page"
/>
</template>
<style scoped>
.margin {
margin-top: 24px;
}
.right {
float: right;
}
</style>

View File

@@ -1,46 +1,46 @@
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Props {
split: "horizontal" | "vertical"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
split: "horizontal",
className: "",
})
const classes = computed(() => [props.split, props.className].join(" "))
</script>
<style scoped>
.splitter-pane.vertical.splitter-paneL {
position: absolute;
left: 0px;
height: 100%;
padding-right: 3px;
}
.splitter-pane.vertical.splitter-paneR {
position: absolute;
right: 0px;
height: 100%;
padding-left: 3px;
}
.splitter-pane.horizontal.splitter-paneL {
position: absolute;
top: 0px;
width: 100%;
}
.splitter-pane.horizontal.splitter-paneR {
position: absolute;
bottom: 0px;
width: 100%;
padding-top: 3px;
}
</style>
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Props {
split: "horizontal" | "vertical"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
split: "horizontal",
className: "",
})
const classes = computed(() => [props.split, props.className].join(" "))
</script>
<style scoped>
.splitter-pane.vertical.splitter-paneL {
position: absolute;
left: 0px;
height: 100%;
padding-right: 3px;
}
.splitter-pane.vertical.splitter-paneR {
position: absolute;
right: 0px;
height: 100%;
padding-left: 3px;
}
.splitter-pane.horizontal.splitter-paneL {
position: absolute;
top: 0px;
width: 100%;
}
.splitter-pane.horizontal.splitter-paneR {
position: absolute;
bottom: 0px;
width: 100%;
padding-top: 3px;
}
</style>

View File

@@ -1,47 +1,47 @@
<template>
<div :class="classes"></div>
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Props {
split: "horizontal" | "vertical"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
split: "horizontal",
className: "",
})
const classes = computed(() =>
["splitter-pane-resizer", props.split, props.className].join(" ")
)
</script>
<style scoped>
.splitter-pane-resizer {
box-sizing: border-box;
background: #000;
position: absolute;
opacity: 0.2;
z-index: 1;
background-clip: padding-box;
}
.splitter-pane-resizer.horizontal {
height: 11px;
margin: -5px 0;
border-top: 5px solid rgba(255, 255, 255, 0);
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
width: 100%;
}
.splitter-pane-resizer.vertical {
width: 11px;
height: 100%;
margin-left: -5px;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
}
</style>
<template>
<div :class="classes"></div>
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Props {
split: "horizontal" | "vertical"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
split: "horizontal",
className: "",
})
const classes = computed(() =>
["splitter-pane-resizer", props.split, props.className].join(" ")
)
</script>
<style scoped>
.splitter-pane-resizer {
box-sizing: border-box;
background: #000;
position: absolute;
opacity: 0.2;
z-index: 1;
background-clip: padding-box;
}
.splitter-pane-resizer.horizontal {
height: 11px;
margin: -5px 0;
border-top: 5px solid rgba(255, 255, 255, 0);
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
width: 100%;
}
.splitter-pane-resizer.vertical {
width: 11px;
height: 100%;
margin-left: -5px;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
}
</style>

View File

@@ -1,141 +1,141 @@
<template>
<div
:style="{ cursor, userSelect }"
class="vue-splitter-container clearfix"
@mouseup="onMouseUp"
@mousemove="onMouseMove"
>
<Pane
class="splitter-pane splitter-paneL"
:split="split"
:style="{ [type]: percent + '%' }"
>
<slot name="panel"></slot>
</Pane>
<Resizer
:className="className"
:style="{ [resizeType]: percent + '%' }"
:split="split"
@mousedown.native="onMouseDown"
@click.native="onClick"
></Resizer>
<Pane
class="splitter-pane splitter-paneR"
:split="split"
:style="{ [type]: 100 - percent + '%' }"
>
<slot name="paner"></slot>
</Pane>
<div class="vue-splitter-container-mask" v-if="active"></div>
</div>
</template>
<script setup lang="ts">
import Resizer from "./Resizer.vue"
import Pane from "./Pane.vue"
import { computed, ref } from "vue"
interface Props {
minPercent?: number
defaultPercent?: number
split: "vertical" | "horizontal"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
minPercent: 10,
defaultPercent: 50,
split: "horizontal",
className: "",
})
const emit = defineEmits(["resize"])
const active = ref(false)
const hasMoved = ref(false)
const percent = ref(props.defaultPercent)
const type = ref(props.split === "vertical" ? "width" : "height")
const resizeType = ref(props.split === "vertical" ? "left" : "top")
const userSelect = computed(() => (active.value ? "none" : "auto"))
const cursor = computed(() =>
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
)
// watch(
// () => defaultPercent,
// (newValue) => {
// percent.value = newValue
// }
// )
function onClick() {
if (!hasMoved.value) {
percent.value = 50
emit("resize", percent.value)
}
}
function onMouseDown() {
active.value = true
hasMoved.value = false
}
function onMouseUp() {
active.value = false
}
function onMouseMove(e: any) {
if (e.buttons === 0) {
active.value = false
}
if (active.value) {
let offset = 0
let target = e.currentTarget
if (props.split === "vertical") {
while (target) {
offset += target.offsetLeft
target = target.offsetParent
}
} else {
while (target) {
offset += target.offsetTop
target = target.offsetParent
}
}
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
const targetOffset =
props.split === "vertical"
? e.currentTarget.offsetWidth
: e.currentTarget.offsetHeight
const newPercent =
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
percent.value = newPercent
}
emit("resize", newPercent)
hasMoved.value = true
}
}
</script>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
position: relative;
}
.vue-splitter-container-mask {
z-index: 9999;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
<template>
<div
:style="{ cursor, userSelect }"
class="vue-splitter-container clearfix"
@mouseup="onMouseUp"
@mousemove="onMouseMove"
>
<Pane
class="splitter-pane splitter-paneL"
:split="split"
:style="{ [type]: percent + '%' }"
>
<slot name="panel"></slot>
</Pane>
<Resizer
:className="className"
:style="{ [resizeType]: percent + '%' }"
:split="split"
@mousedown.native="onMouseDown"
@click.native="onClick"
></Resizer>
<Pane
class="splitter-pane splitter-paneR"
:split="split"
:style="{ [type]: 100 - percent + '%' }"
>
<slot name="paner"></slot>
</Pane>
<div class="vue-splitter-container-mask" v-if="active"></div>
</div>
</template>
<script setup lang="ts">
import Resizer from "./Resizer.vue"
import Pane from "./Pane.vue"
import { computed, ref } from "vue"
interface Props {
minPercent?: number
defaultPercent?: number
split: "vertical" | "horizontal"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
minPercent: 10,
defaultPercent: 50,
split: "horizontal",
className: "",
})
const emit = defineEmits(["resize"])
const active = ref(false)
const hasMoved = ref(false)
const percent = ref(props.defaultPercent)
const type = ref(props.split === "vertical" ? "width" : "height")
const resizeType = ref(props.split === "vertical" ? "left" : "top")
const userSelect = computed(() => (active.value ? "none" : "auto"))
const cursor = computed(() =>
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
)
// watch(
// () => defaultPercent,
// (newValue) => {
// percent.value = newValue
// }
// )
function onClick() {
if (!hasMoved.value) {
percent.value = 50
emit("resize", percent.value)
}
}
function onMouseDown() {
active.value = true
hasMoved.value = false
}
function onMouseUp() {
active.value = false
}
function onMouseMove(e: any) {
if (e.buttons === 0) {
active.value = false
}
if (active.value) {
let offset = 0
let target = e.currentTarget
if (props.split === "vertical") {
while (target) {
offset += target.offsetLeft
target = target.offsetParent
}
} else {
while (target) {
offset += target.offsetTop
target = target.offsetParent
}
}
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
const targetOffset =
props.split === "vertical"
? e.currentTarget.offsetWidth
: e.currentTarget.offsetHeight
const newPercent =
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
percent.value = newPercent
}
emit("resize", newPercent)
hasMoved.value = true
}
}
</script>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
position: relative;
}
.vue-splitter-container-mask {
z-index: 9999;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>

View File

@@ -1,6 +1,6 @@
import { breakpointsTailwind } from "@vueuse/core"
const breakpoints = useBreakpoints(breakpointsTailwind)
export const isMobile = breakpoints.smallerOrEqual("md")
export const isDesktop = breakpoints.greater("md")
import { breakpointsTailwind } from "@vueuse/core"
const breakpoints = useBreakpoints(breakpointsTailwind)
export const isMobile = breakpoints.smallerOrEqual("md")
export const isDesktop = breakpoints.greater("md")

View File

@@ -1,2 +1,2 @@
export const isDark = useDark({ storageKey: "theme-appearance" })
export const toggleDark = useToggle(isDark)
export const isDark = useDark({ storageKey: "theme-appearance" })
export const toggleDark = useToggle(isDark)

View File

@@ -1,2 +1,2 @@
export const [loginModal, toggleLogin] = useToggle()
export const [signupModal, toggleSignup] = useToggle()
export const [loginModal, toggleLogin] = useToggle()
export const [signupModal, toggleSignup] = useToggle()

View File

@@ -1,11 +1,11 @@
<script setup lang="ts"></script>
<template>
<el-container>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</template>
<style scoped></style>
<script setup lang="ts"></script>
<template>
<el-container>
<el-main>
<router-view></router-view>
</el-main>
</el-container>
</template>
<style scoped></style>

View File

@@ -1,24 +1,24 @@
<script setup lang="ts">
import Login from "../Login/index.vue"
import Signup from "../Signup/index.vue"
import Header from "../Header/index.vue"
</script>
<template>
<el-container>
<el-header class="header">
<Header />
</el-header>
<el-main>
<router-view></router-view>
</el-main>
<Login />
<Signup />
</el-container>
</template>
<style scoped>
.header {
display: flex;
}
</style>
<script setup lang="ts">
import Login from "../Login/index.vue"
import Signup from "../Signup/index.vue"
import Header from "../Header/index.vue"
</script>
<template>
<el-container>
<el-header class="header">
<Header />
</el-header>
<el-main>
<router-view></router-view>
</el-main>
<Login />
<Signup />
</el-container>
</template>
<style scoped>
.header {
display: flex;
}
</style>

View File

@@ -1,98 +1,98 @@
<script setup lang="ts">
import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types"
import { isMobile } from "~/shared/composables/breakpoints"
import { isDark } from "../composables/dark"
interface Props {
value: string
language?: LANGUAGE
height?: string
fontSize?: number
class?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
height: "calc(100vh - 100px)",
fontSize: 20,
class: "",
})
const emit = defineEmits<{
(e: "change", value: string): void
}>()
const monacoEditorRef = ref()
let editor: Monaco.editor.IStandaloneCodeEditor
onMounted(function () {
const model = window.monaco.editor.createModel(
props.value,
LANGUAGE_VALUE[props.language]
)
editor = window.monaco.editor.create(monacoEditorRef.value, {
model,
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: {
enabled: false,
},
lineNumbersMinChars: 3,
automaticLayout: true, // 自适应布局
tabSize: 4,
fontSize: isMobile.value ? 20 : 24, // 字体大小
scrollBeyondLastLine: false,
lineDecorationsWidth: 0,
scrollBeyondLastColumn: 0,
glyphMargin: false,
scrollbar: {
useShadows: false,
vertical: "hidden",
horizontal: "hidden",
},
overviewRulerLanes: 0,
})
model.onDidChangeContent(() => {
const value = model.getValue().toString()
emit("change", value)
})
editor.onKeyDown((e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
e.preventDefault()
}
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
e.preventDefault()
}
})
watchEffect(() => {
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
})
watchEffect(() => {
if (props.value !== model.getValue()) {
model.setValue(props.value)
}
})
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
})
onUnmounted(() => {
editor && editor.dispose()
})
</script>
<template>
<div
ref="monacoEditorRef"
:class="props.class"
:style="{ height: props.height }"
></div>
</template>
<style scoped></style>
<script setup lang="ts">
import type * as Monaco from "monaco-editor"
import { LANGUAGE_VALUE } from "utils/constants"
import { LANGUAGE } from "utils/types"
import { isMobile } from "~/shared/composables/breakpoints"
import { isDark } from "../composables/dark"
interface Props {
value: string
language?: LANGUAGE
height?: string
fontSize?: number
class?: string
}
const props = withDefaults(defineProps<Props>(), {
language: "C",
height: "calc(100vh - 100px)",
fontSize: 20,
class: "",
})
const emit = defineEmits<{
(e: "change", value: string): void
}>()
const monacoEditorRef = ref()
let editor: Monaco.editor.IStandaloneCodeEditor
onMounted(function () {
const model = window.monaco.editor.createModel(
props.value,
LANGUAGE_VALUE[props.language]
)
editor = window.monaco.editor.create(monacoEditorRef.value, {
model,
theme: isDark.value ? "dark" : "light", // 官方自带三种主题vs, hc-black, or vs-dark
minimap: {
enabled: false,
},
lineNumbersMinChars: 3,
automaticLayout: true, // 自适应布局
tabSize: 4,
fontSize: isMobile.value ? 20 : 24, // 字体大小
scrollBeyondLastLine: false,
lineDecorationsWidth: 0,
scrollBeyondLastColumn: 0,
glyphMargin: false,
scrollbar: {
useShadows: false,
vertical: "hidden",
horizontal: "hidden",
},
overviewRulerLanes: 0,
})
model.onDidChangeContent(() => {
const value = model.getValue().toString()
emit("change", value)
})
editor.onKeyDown((e) => {
if ((e.ctrlKey || e.metaKey) && e.code === "KeyS") {
e.preventDefault()
}
if ((e.ctrlKey || e.metaKey) && e.code === "KeyR") {
e.preventDefault()
}
})
watchEffect(() => {
window.monaco.editor.setModelLanguage(model, LANGUAGE_VALUE[props.language])
})
watchEffect(() => {
if (props.value !== model.getValue()) {
model.setValue(props.value)
}
})
watchEffect(() => {
window.monaco.editor.setTheme(isDark.value ? "dark" : "light")
})
})
onUnmounted(() => {
editor && editor.dispose()
})
</script>
<template>
<div
ref="monacoEditorRef"
:class="props.class"
:style="{ height: props.height }"
></div>
</template>
<style scoped></style>

View File

@@ -1,141 +1,141 @@
<template>
<div
:style="{ cursor, userSelect }"
class="vue-splitter-container clearfix"
@mouseup="onMouseUp"
@mousemove="onMouseMove"
>
<Pane
class="splitter-pane splitter-paneL"
:split="split"
:style="{ [type]: percent + '%' }"
>
<slot name="panel"></slot>
</Pane>
<Resizer
:className="className"
:style="{ [resizeType]: percent + '%' }"
:split="split"
@mousedown.native="onMouseDown"
@click.native="onClick"
></Resizer>
<Pane
class="splitter-pane splitter-paneR"
:split="split"
:style="{ [type]: 100 - percent + '%' }"
>
<slot name="paner"></slot>
</Pane>
<div class="vue-splitter-container-mask" v-if="active"></div>
</div>
</template>
<script setup lang="ts">
import Resizer from "./Resizer.vue"
import Pane from "./Pane.vue"
import { computed, ref } from "vue"
interface Props {
minPercent?: number
defaultPercent?: number
split: "vertical" | "horizontal"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
minPercent: 10,
defaultPercent: 50,
split: "horizontal",
className: "",
})
const emit = defineEmits(["resize"])
const active = ref(false)
const hasMoved = ref(false)
const percent = ref(props.defaultPercent)
const type = ref(props.split === "vertical" ? "width" : "height")
const resizeType = ref(props.split === "vertical" ? "left" : "top")
const userSelect = computed(() => (active.value ? "none" : "auto"))
const cursor = computed(() =>
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
)
// watch(
// () => defaultPercent,
// (newValue) => {
// percent.value = newValue
// }
// )
function onClick() {
if (!hasMoved.value) {
percent.value = 50
emit("resize", percent.value)
}
}
function onMouseDown() {
active.value = true
hasMoved.value = false
}
function onMouseUp() {
active.value = false
}
function onMouseMove(e: any) {
if (e.buttons === 0) {
active.value = false
}
if (active.value) {
let offset = 0
let target = e.currentTarget
if (props.split === "vertical") {
while (target) {
offset += target.offsetLeft
target = target.offsetParent
}
} else {
while (target) {
offset += target.offsetTop
target = target.offsetParent
}
}
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
const targetOffset =
props.split === "vertical"
? e.currentTarget.offsetWidth
: e.currentTarget.offsetHeight
const newPercent =
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
percent.value = newPercent
}
emit("resize", newPercent)
hasMoved.value = true
}
}
</script>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
position: relative;
}
.vue-splitter-container-mask {
z-index: 9999;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>
<template>
<div
:style="{ cursor, userSelect }"
class="vue-splitter-container clearfix"
@mouseup="onMouseUp"
@mousemove="onMouseMove"
>
<Pane
class="splitter-pane splitter-paneL"
:split="split"
:style="{ [type]: percent + '%' }"
>
<slot name="panel"></slot>
</Pane>
<Resizer
:className="className"
:style="{ [resizeType]: percent + '%' }"
:split="split"
@mousedown.native="onMouseDown"
@click.native="onClick"
></Resizer>
<Pane
class="splitter-pane splitter-paneR"
:split="split"
:style="{ [type]: 100 - percent + '%' }"
>
<slot name="paner"></slot>
</Pane>
<div class="vue-splitter-container-mask" v-if="active"></div>
</div>
</template>
<script setup lang="ts">
import Resizer from "./Resizer.vue"
import Pane from "./Pane.vue"
import { computed, ref } from "vue"
interface Props {
minPercent?: number
defaultPercent?: number
split: "vertical" | "horizontal"
className?: string
}
const props = withDefaults(defineProps<Props>(), {
minPercent: 10,
defaultPercent: 50,
split: "horizontal",
className: "",
})
const emit = defineEmits(["resize"])
const active = ref(false)
const hasMoved = ref(false)
const percent = ref(props.defaultPercent)
const type = ref(props.split === "vertical" ? "width" : "height")
const resizeType = ref(props.split === "vertical" ? "left" : "top")
const userSelect = computed(() => (active.value ? "none" : "auto"))
const cursor = computed(() =>
active.value ? (props.split === "vertical" ? "col-resize" : "row-resize") : ""
)
// watch(
// () => defaultPercent,
// (newValue) => {
// percent.value = newValue
// }
// )
function onClick() {
if (!hasMoved.value) {
percent.value = 50
emit("resize", percent.value)
}
}
function onMouseDown() {
active.value = true
hasMoved.value = false
}
function onMouseUp() {
active.value = false
}
function onMouseMove(e: any) {
if (e.buttons === 0) {
active.value = false
}
if (active.value) {
let offset = 0
let target = e.currentTarget
if (props.split === "vertical") {
while (target) {
offset += target.offsetLeft
target = target.offsetParent
}
} else {
while (target) {
offset += target.offsetTop
target = target.offsetParent
}
}
const currentPage = props.split === "vertical" ? e.pageX : e.pageY
const targetOffset =
props.split === "vertical"
? e.currentTarget.offsetWidth
: e.currentTarget.offsetHeight
const newPercent =
Math.floor(((currentPage - offset) / targetOffset) * 10000) / 100
if (newPercent > props.minPercent && newPercent < 100 - props.minPercent) {
percent.value = newPercent
}
emit("resize", newPercent)
hasMoved.value = true
}
}
</script>
<style scoped>
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.vue-splitter-container {
height: 100%;
position: relative;
}
.vue-splitter-container-mask {
z-index: 9999;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
</style>