Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: universal Modal component #666

Merged
merged 7 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions apps/client/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@
</div>
</div>
</header>
<MainMessageBox
v-model:isShowModal="isShowModal"
title="提示"
content="是否确认退出登录?"
@confirm="signOut()"
/>

<UserMenu
v-model:open="isOpenUserMenu"
@logout="handleLogout"
>
</UserMenu>
/>
<MainMessageBox
v-model:show-modal="isShowModal"
content="是否确认退出登录?"
confirm-btn-text="确认"
@confirm="signOut"
/>
</template>

<script setup lang="ts">
Expand Down Expand Up @@ -117,6 +117,7 @@ const HEADER_OPTIONS = [
{ name: "联系我们", href: "#contact" },
];

// TODO: 设置需要固定导航栏的页面
const isStickyNavBar = computed(() => ["index", "User-Setting"].includes(route.name as string));
const isScrolled = computed(() => y.value >= SCROLL_THRESHOLD);

Expand Down
113 changes: 113 additions & 0 deletions apps/client/components/common/Modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<script lang="ts" setup>
import { ref, watchEffect } from "vue";

defineOptions({ name: "Modal" });

/**
* @param {boolean} showModal - 必传,是否显示弹窗,默认不显示
* @param {boolean} modal - 可选,是否显示遮罩层,默认不显示
* @param {string} modalColor - 可选,设置遮罩层背景色,默认 30% 黑色透明
* @param {string} offsetTop - 可选,距离顶部的偏移量,默认 -8vh,因为默认使用 modal-middle 只能往上走来调整
* @param {string} twClass - 可选,给 modal-box 补充一些 Tailwind CSS
* @param {boolean} closeOnClickModal - 可选,是否允许点击遮罩层关闭弹窗,默认不允许
*/
const props = withDefaults(
defineProps<{
showModal: boolean;
modal?: boolean;
modalColor?: string;
offsetTop?: string;
twClass?: string;
closeOnClickModal?: boolean;
}>(),
{
showModal: false,
modal: false,
modalColor: "rgba(0, 0, 0, 0.3)",
offsetTop: "-8vh",
twClass: "",
closeOnClickModal: false,
},
);

const emits = defineEmits(["close"]);

const modalRef = ref<HTMLDialogElement | null>(null);

// 检查 modalRef 是否存在
function checkModalRef() {
return !!modalRef.value;
}

function show() {
modalRef.value?.show();
}

function close() {
modalRef.value?.close();
}

function showModal() {
modalRef.value?.showModal();
}

function handleClick(e: MouseEvent) {
if (!checkModalRef()) return;

if (props.closeOnClickModal && e.target === modalRef.value) {
handleClose();
}
}

function handleOpen() {
if (!checkModalRef()) return;

props.modal ? showModal() : show();
}

function handleClose() {
if (!checkModalRef()) return;

close();
emits("close");
}

watchEffect(() => {
if (!checkModalRef()) return;

// 处理外层传入的 showModal 改变时,控制弹框的显示/隐藏
if (props.showModal) {
props.modal ? showModal() : show();
} else {
close();
}
});

// 外部可以设置 ref 后调用 open/close 方法控制弹框
defineExpose({
open: handleOpen,
close: handleClose,
});
</script>

<template>
<dialog
ref="modalRef"
class="modal"
@click="handleClick"
>
<div
class="modal-box bg-white dark:bg-gray-800"
:class="twClass"
:style="{ marginTop: offsetTop }"
>
<slot />
</div>
</dialog>
</template>

<style scoped>
dialog::backdrop {
background-color: v-bind(modalColor);
}
</style>
2 changes: 1 addition & 1 deletion apps/client/components/common/ProgressBar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="rounded-lg border dark:border-slate-400">
<div class="rounded-lg border border-gray-300 dark:border-gray-600">
<div
class="h-full rounded-lg bg-gradient-to-r from-emerald-200 to-emerald-400 transition-all dark:from-emerald-300 dark:to-emerald-500"
:style="{ width: `${percentage}%` }"
Expand Down
7 changes: 2 additions & 5 deletions apps/client/components/main/AuthRequired.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<template>
<dialog
className="modal top-[-8vh]"
:open="authRequireModalState"
>
<CommonModal :show-modal="authRequireModalState">
<div className="modal-box">
<h3 className="font-bold text-lg mb-4">✨ 友情提示</h3>
<p class="py-4 text-center text-xl">注册以进行下一课的学习哦~ 😊</p>
Expand All @@ -21,7 +18,7 @@
</button>
</div>
</div>
</dialog>
</CommonModal>
</template>

<script setup lang="ts">
Expand Down
15 changes: 7 additions & 8 deletions apps/client/components/main/Game.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@
<MainSummary />
<MainShare />
<MainAuthRequired />
<MessageBox
:content="messageContent"
v-model:isShowModal="isMessageShow"
<!-- TODO: 暂时先不提示(有些用户正在移动端的场景下使用)-->
<!-- <MainMessageBox
v-model:show-modal="isMessageShow"
cancel-btn-text="确定"
confirmBtnText=""
></MessageBox>
:content="messageContent"
/> -->
</template>

<script setup lang="ts">
import { onMounted } from "vue";

import { courseTimer } from "~/composables/courses/courseTimer";
import { useDeviceTip } from "~/composables/main/game";
// import { useDeviceTip } from "~/composables/main/game";
import { GameMode, useGameMode } from "~/composables/user/gameMode";
import MessageBox from "./MessageBox/MessageBox.vue";

const { isMessageShow, messageContent } = useDeviceTip();
// const { isMessageShow, messageContent } = useDeviceTip();
const { currentGameMode } = useGameMode();

onMounted(() => {
Expand Down
63 changes: 63 additions & 0 deletions apps/client/components/main/MessageBox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script setup lang="ts">
import { useVModel } from "@vueuse/core";

const props = withDefaults(
defineProps<{
showModal: boolean;
modal?: boolean;
title?: string;
content?: string;
cancelBtnText?: string;
confirmBtnText?: string;
}>(),
{
showModal: false,
modal: true,
title: "提示",
content: "你确定吗?",
cancelBtnText: "取消",
confirmBtnText: "",
},
);
const emits = defineEmits(["confirm", "update:showModal"]);

// 可以在这个地方直接更新外层 showModal
const isShowModal = useVModel(props, "showModal", emits);

function handleCancel() {
isShowModal.value = false;
}

function handleConfirm() {
emits("confirm");
handleCancel();
}
</script>

<template>
<CommonModal
:show-modal="isShowModal"
:modal="modal"
:close-on-click-modal="true"
@close="handleCancel"
>
<h3 class="text-lg font-bold">{{ title }}</h3>
<p class="py-4">{{ content }}</p>
<div class="modal-action">
<button
class="btn mr-2"
@click="handleCancel"
>
{{ cancelBtnText }}
</button>
<!-- TODO: 后续看看有没有更好的方案 -->
<button
v-if="confirmBtnText"
class="btn"
@click="handleConfirm"
>
{{ confirmBtnText }}
</button>
</div>
</CommonModal>
</template>
48 changes: 0 additions & 48 deletions apps/client/components/main/MessageBox/MessageBox.vue

This file was deleted.

59 changes: 0 additions & 59 deletions apps/client/components/main/MessageBox/tests/message-box.spec.ts

This file was deleted.

Loading
Loading