Skip to content

Commit

Permalink
Add Drawer component (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
mosarabi authored Jan 26, 2024
1 parent 3818a7b commit 30a5e33
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 50 deletions.
71 changes: 28 additions & 43 deletions packages/vue3/src/_dev/App.vue
Original file line number Diff line number Diff line change
@@ -1,63 +1,48 @@
<script setup lang="ts">
import 'uno.css';
import '../assets/main.css';
import { ref, watch } from 'vue';
import { MediaInput, UploadedMedia } from '~/components';
import { ref } from 'vue';
import { Drawer, PrimaryButton } from '~/components';
const attachments = ref<File[]>([]);
const disabled = ref(false);
const limit = 4;
const checkLimit = () => {
disabled.value = attachments.value.length >= limit;
};
const deleteFile = (file: File) => {
const idx = attachments.value.indexOf(file);
if (idx > -1) {
attachments.value.splice(idx, 1);
checkLimit();
}
};
watch(attachments, () => {
checkLimit();
});
const show = ref(false);
</script>

<template>
<Drawer v-model:visible="show" position="left">
<p v-for="index in 50" :key="index" class="content">
The quick brown fox jumps over the lazy dog.
</p>
<template #footer>
<div class="actions">
<PrimaryButton @click="show = false;">
<span>Save</span>
</PrimaryButton>
</div>
</template>
</Drawer>
<div class="container">
<div class="files-grid">
<UploadedMedia
v-for="(attachment, index) in attachments"
:key="index"
:file="attachment"
@delete="deleteFile(attachment)"
/>
</div>
<MediaInput v-model="attachments" :limit="limit" :disabled="disabled" />
<div class="files-grid">
<UploadedMedia
file="https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885_1280.jpg"
/>
</div>
<PrimaryButton @click="show = true;">
<span>Open Drawer</span>
</PrimaryButton>
</div>
</template>

<style scoped>
.container {
display: flex;
position: relative;
flex-direction: column;
align-items: center;
justify-content: center;
width: 300px;
min-height: 140px;
width: 100vw;
height: 100vh;
}
.file-grid {
position: absolute;
right: 0;
bottom: 0;
left: 0;
.content {
margin: 0;
text-align: center;
}
.actions {
display: flex;
flex-direction: row-reverse;
}
</style>
212 changes: 212 additions & 0 deletions packages/vue3/src/components/Drawer/Drawer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
<script lang="ts" setup>
import { onMounted, onUnmounted, useSlots } from 'vue';
import type { DrawerProps } from './types';
import Overlay from '~/components/Overlay/Overlay.vue';
const props = withDefaults(defineProps<DrawerProps>(), {
visible: false,
position: 'right',
});
const emit = defineEmits(['update:visible']);
const slots = useSlots();
const close = () => {
emit('update:visible', false);
};
const handleKeypress = (event: KeyboardEvent) => {
if (props.visible && event.key === 'Escape') {
close();
}
};
onMounted(() => {
window.addEventListener('keydown', handleKeypress);
});
onUnmounted(() => {
window.removeEventListener('keydown', handleKeypress);
});
</script>

<template>
<Transition name="fade">
<Overlay v-show="visible" class="drawer-overlay" :class="position" @on-backdrop-click="close">
<Transition :name="position">
<div v-if="visible" class="drawer">
<div class="header">
<div class="close" @click="close">
<i class="i-youcan-x" />
</div>
</div>
<div class="body">
<slot />
</div>
<div v-if="slots.footer" class="footer">
<slot name="footer" />
</div>
</div>
</Transition>
</Overlay>
</Transition>
</template>

<style scoped lang="scss">
$duration: 0.45s;
.drawer-overlay {
flex-direction: row-reverse;
align-items: flex-start;
.drawer {
display: flex;
flex: 1;
flex-direction: column;
width: max-content;
max-width: 80vw;
overflow: hidden;
border: 1px solid var(--gray-200);
background-color: var(--base-white);
box-shadow: var(--shadow-md-gray);
@media only screen and (max-width: 992px) {
max-width: 100vw;
}
> * {
box-sizing: border-box;
width: 100%;
}
.header {
display: flex;
flex-direction: row;
align-items: center;
padding: 10px 20px;
border-bottom: 1px solid var(--gray-100);
.close {
cursor: pointer;
i {
color: var(--gray-500);
}
}
}
.footer {
padding: 10px 20px;
border-top: 1px solid var(--gray-100);
}
.body {
flex: 1;
padding: 14px 20px;
overflow-y: auto;
font: var(--text-md-regular);
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 2px;
background-color: var(--brand-500);
}
&::-webkit-scrollbar-thumb:hover {
background-color: var(--brand-500);
}
}
}
&.left {
flex-direction: row;
.drawer {
.header {
flex-direction: row-reverse;
}
}
}
> :deep(.body) {
display: flex;
flex-direction: column;
height: 100%;
margin: 0;
}
}
.fade-enter-active {
animation: fade $duration ease-in-out;
}
.fade-leave-active {
animation: fade $duration reverse ease-in-out;
}
@keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.right-enter-active {
animation: right $duration ease-in-out;
}
.right-leave-active {
animation: right $duration reverse ease-in-out;
}
@keyframes right {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(0%);
}
}
.left-enter-active {
animation: left $duration ease-in-out;
}
.left-leave-active {
animation: left $duration reverse ease-in-out;
}
@keyframes left {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
html[dir="rtl"] {
.drawer-overlay {
flex-direction: row;
align-items: flex-start;
direction: rtl;
&.left {
flex-direction: row-reverse;
}
}
}
</style>
4 changes: 4 additions & 0 deletions packages/vue3/src/components/Drawer/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface DrawerProps {
visible?: boolean
position?: 'left' | 'right'
}
27 changes: 22 additions & 5 deletions packages/vue3/src/components/Modal/Modal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ const props = withDefaults(defineProps<ModalProps>(), {
});
const emit = defineEmits(['update:visible', 'onConfirm']);
const closeModal = () => {
const close = () => {
emit('update:visible', false);
};
const handleKeypress = (event: KeyboardEvent) => {
if (props.visible && event.key === 'Escape') {
closeModal();
close();
}
};
Expand All @@ -31,11 +31,11 @@ onUnmounted(() => {

<template>
<Transition name="fade">
<Overlay v-show="visible" @on-backdrop-click="closeModal">
<Overlay v-show="visible" @on-backdrop-click="close">
<Transition name="slide-up">
<div v-if="visible" class="modal">
<div class="header">
<TertiaryButton @click="closeModal">
<TertiaryButton @click="close">
<i class="i-youcan-x" />
</TertiaryButton>
<span class="title">{{ title }}</span>
Expand All @@ -50,7 +50,7 @@ onUnmounted(() => {
</template>
<span>{{ confirmLabel }}</span>
</PrimaryButton>
<SecondaryButton @click="closeModal">
<SecondaryButton @click="close">
<span>{{ cancelLabel }}</span>
</SecondaryButton>
</div>
Expand Down Expand Up @@ -110,6 +110,23 @@ onUnmounted(() => {
padding: 20px;
overflow-y: auto;
font: var(--text-md-regular);
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
border-radius: 2px;
background-color: var(--brand-500);
}
&::-webkit-scrollbar-thumb:hover {
background-color: var(--brand-500);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vue3/src/components/Overlay/Overlay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const emit = defineEmits(['onBackdropClick']);
position: relative;
z-index: 3;
max-width: 100%;
margin: auto;
margin: 0 auto;
}
}
</style>
2 changes: 1 addition & 1 deletion packages/vue3/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export { default as SearchInput } from './Search/SearchInput.vue';
export { default as Alert } from './Alert/Alert.vue';
export { default as Toast } from './Toast/Toast.vue';
export { default as ToastContainer } from './ToastContainer/ToastContainer.vue';
export { toast } from './ToastContainer/toast';
export { default as DraggableItem } from './Draggable/DraggableItem.vue';
export { default as Draggable } from './Draggable/Draggable.vue';
export { default as Radio } from './Radio/Radio.vue';
Expand All @@ -63,3 +62,4 @@ export { default as Skeleton } from './Skeleton/Skeleton.vue';
export { default as Spinner } from './Spinner/Spinner.vue';
export { default as MultiSwitch } from './MultiSwitch/MultiSwitch.vue';
export { default as SwitchButton } from './MultiSwitch/SwitchButton.vue';
export { default as Drawer } from './Drawer/Drawer.vue';

0 comments on commit 30a5e33

Please sign in to comment.