feat: pure dialog (non-bloat) component
This commit is contained in:
parent
301dcc32aa
commit
779374b164
3 changed files with 135 additions and 0 deletions
83
src/components/dialog/DialogContainer.vue
Normal file
83
src/components/dialog/DialogContainer.vue
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps<{
|
||||||
|
width?: string;
|
||||||
|
height?: string;
|
||||||
|
maxWidth?: string;
|
||||||
|
footer?: boolean;
|
||||||
|
// Cannot emit as it cannot control when user wanted to interrupt close state (e.g. remain open)
|
||||||
|
onClose?: (fn: (state?: boolean) => void, ...args: unknown[]) => void;
|
||||||
|
onOpen?: (fn: (state?: boolean) => void, ...args: unknown[]) => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function update(value: boolean) {
|
||||||
|
if (value === false) {
|
||||||
|
if (props.onClose) {
|
||||||
|
props.onClose((v) => (state.value = v === undefined ? false : v));
|
||||||
|
} else {
|
||||||
|
state.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = defineModel({ default: false });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-dialog
|
||||||
|
:model-value="state"
|
||||||
|
@update:model-value="update"
|
||||||
|
@before-show="() => onOpen?.((v) => (state = v === undefined ? true : v))"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="surface-1 rounded"
|
||||||
|
:style="{
|
||||||
|
borderRadius: 'var(--radius-2)',
|
||||||
|
padding: '0',
|
||||||
|
width: $q.screen.xs ? '100%' : width ? width : '85%',
|
||||||
|
height: height ? height : 'calc(100vh - 64px)',
|
||||||
|
maxWidth: $q.screen.xs ? '100%' : maxWidth ? maxWidth : '85%',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="column full-height">
|
||||||
|
<!-- NOTE: DIALOG HEADER -->
|
||||||
|
<div class="form-header q-py-sm q-px-lg">
|
||||||
|
<slot name="header" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NOTE: DIALOG BODY -->
|
||||||
|
<div
|
||||||
|
class="col full-height column full-width"
|
||||||
|
:class="{ dark: $q.dark.isActive }"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- NOTE: DIALOG FOOTER -->
|
||||||
|
<div
|
||||||
|
v-if="footer || $slots.footer"
|
||||||
|
class="dialog-footer row items-center full-width justify-between q-px-md q-py-md surface-1"
|
||||||
|
>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.form-header {
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-body {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
src/components/dialog/DialogHeader.vue
Normal file
50
src/components/dialog/DialogHeader.vue
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineProps<{
|
||||||
|
title: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="dialog-header-container">
|
||||||
|
<div class="dialog-header-main">
|
||||||
|
<slot name="title-before" />
|
||||||
|
{{ title }}
|
||||||
|
<slot name="title-after" />
|
||||||
|
</div>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
unelevated
|
||||||
|
icon="mdi-close"
|
||||||
|
padding="xs"
|
||||||
|
class="dialog-header-close"
|
||||||
|
:class="{ dark: $q.dark.isActive }"
|
||||||
|
v-close-popup
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ $t('close') }}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.dialog-header-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header-main {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header-close {
|
||||||
|
color: hsl(var(--negative-bg));
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
&.dark {
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid hsl(var(--negative-bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
2
src/components/dialog/index.ts
Normal file
2
src/components/dialog/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as DialogContainer } from './DialogContainer.vue';
|
||||||
|
export { default as DialogHeader } from './DialogHeader.vue';
|
||||||
Loading…
Add table
Add a link
Reference in a new issue