@teammodlr/ui-kit 3.0.0-beta.17 → 3.0.0-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/dialog/Dialog.vue.d.ts +1 -0
- package/lib/components/dialogprovider/types.d.ts +2 -0
- package/lib/components/drawer/Drawer.vue.d.ts +142 -0
- package/lib/components/drawer/style/Drawer.css.d.ts +33 -0
- package/lib/components/drawer/style/index.d.ts +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/index.cjs.js +3 -3
- package/lib/index.es.js +4806 -4483
- package/lib/style.css +1 -1
- package/package.json +1 -1
- package/src/components/dialog/Dialog.vue +11 -3
- package/src/components/dialogprovider/DialogProvider.vue +7 -1
- package/src/components/dialogprovider/types.ts +2 -0
- package/src/components/drawer/Drawer.vue +276 -0
- package/src/components/drawer/examples/basic.vue +15 -0
- package/src/components/drawer/examples/custom-title.vue +21 -0
- package/src/components/drawer/examples/index.stories.ts +24 -0
- package/src/components/drawer/examples/index.ts +7 -0
- package/src/components/drawer/examples/no-mask.vue +15 -0
- package/src/components/drawer/examples/no-outside-close.vue +15 -0
- package/src/components/drawer/examples/placements.vue +28 -0
- package/src/components/drawer/examples/with-footer.vue +21 -0
- package/src/components/drawer/examples/with-trigger.vue +13 -0
- package/src/components/drawer/style/Drawer.css.ts +208 -0
- package/src/components/drawer/style/index.ts +1 -0
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<DialogRoot :open="isOpen" modal @update:open="handleOpenChange">
|
|
3
|
+
<DialogTrigger v-if="hasTrigger" as-child>
|
|
4
|
+
<slot name="trigger" />
|
|
5
|
+
</DialogTrigger>
|
|
6
|
+
<DialogPortal to="body">
|
|
7
|
+
<Transition
|
|
8
|
+
appear
|
|
9
|
+
type="animation"
|
|
10
|
+
:enter-active-class="fade.enterActive"
|
|
11
|
+
:leave-active-class="fade.leaveActive"
|
|
12
|
+
:enter-from-class="fade.enterFrom"
|
|
13
|
+
:leave-to-class="fade.leaveTo"
|
|
14
|
+
>
|
|
15
|
+
<DialogOverlay
|
|
16
|
+
v-if="isOpen && showMask"
|
|
17
|
+
force-mount
|
|
18
|
+
:class="style.mask"
|
|
19
|
+
:style="{ zIndex }"
|
|
20
|
+
/>
|
|
21
|
+
</Transition>
|
|
22
|
+
<Transition
|
|
23
|
+
appear
|
|
24
|
+
type="animation"
|
|
25
|
+
:enter-active-class="currentTransition.enterActive"
|
|
26
|
+
:leave-active-class="currentTransition.leaveActive"
|
|
27
|
+
:enter-from-class="currentTransition.enterFrom"
|
|
28
|
+
:leave-to-class="currentTransition.leaveTo"
|
|
29
|
+
>
|
|
30
|
+
<DialogContent
|
|
31
|
+
v-if="isOpen"
|
|
32
|
+
force-mount
|
|
33
|
+
:class="[style.placement[placement]]"
|
|
34
|
+
:style="contentStyle"
|
|
35
|
+
:disable-outside-pointer-events="true"
|
|
36
|
+
@open-auto-focus="handleOpenAutoFocus"
|
|
37
|
+
@escape-key-down="onEscapeKeyDown"
|
|
38
|
+
@pointer-down-outside="onPointerDownOutside"
|
|
39
|
+
@interact-outside="onInteractOutside"
|
|
40
|
+
>
|
|
41
|
+
<FocusScope :trapped="focusTrap" :loop="focusTrap">
|
|
42
|
+
<div v-if="hasTitle || showClose" :class="style.header">
|
|
43
|
+
<DialogTitle as-child>
|
|
44
|
+
<div :class="style.title">
|
|
45
|
+
<slot name="title">{{ title }}</slot>
|
|
46
|
+
</div>
|
|
47
|
+
</DialogTitle>
|
|
48
|
+
<DialogClose v-if="showClose" as-child>
|
|
49
|
+
<button :class="style.close" aria-label="Close" />
|
|
50
|
+
</DialogClose>
|
|
51
|
+
</div>
|
|
52
|
+
<div :class="style.body" ref="bodyRef">
|
|
53
|
+
<slot />
|
|
54
|
+
</div>
|
|
55
|
+
<div v-if="hasFooter" :class="style.footer">
|
|
56
|
+
<slot name="footer" />
|
|
57
|
+
</div>
|
|
58
|
+
</FocusScope>
|
|
59
|
+
</DialogContent>
|
|
60
|
+
</Transition>
|
|
61
|
+
</DialogPortal>
|
|
62
|
+
</DialogRoot>
|
|
63
|
+
</template>
|
|
64
|
+
|
|
65
|
+
<script lang="ts">
|
|
66
|
+
import type { CSSProperties, PropType } from 'vue';
|
|
67
|
+
import {
|
|
68
|
+
DialogClose,
|
|
69
|
+
DialogContent,
|
|
70
|
+
DialogOverlay,
|
|
71
|
+
DialogPortal,
|
|
72
|
+
DialogRoot,
|
|
73
|
+
DialogTitle,
|
|
74
|
+
DialogTrigger,
|
|
75
|
+
FocusScope
|
|
76
|
+
} from 'reka-ui';
|
|
77
|
+
import { computed, defineComponent, provide, ref, watch } from 'vue';
|
|
78
|
+
import * as style from './style';
|
|
79
|
+
import { fade } from '../../styles/transitions.css';
|
|
80
|
+
import { useZIndex, ZINDEX } from '../../composables/useZIndex';
|
|
81
|
+
|
|
82
|
+
type Placement = 'left' | 'right' | 'top' | 'bottom';
|
|
83
|
+
|
|
84
|
+
export default defineComponent({
|
|
85
|
+
name: 'MDrawer',
|
|
86
|
+
components: {
|
|
87
|
+
DialogClose,
|
|
88
|
+
DialogContent,
|
|
89
|
+
DialogOverlay,
|
|
90
|
+
DialogPortal,
|
|
91
|
+
DialogRoot,
|
|
92
|
+
DialogTitle,
|
|
93
|
+
DialogTrigger,
|
|
94
|
+
FocusScope
|
|
95
|
+
},
|
|
96
|
+
props: {
|
|
97
|
+
open: {
|
|
98
|
+
type: Boolean,
|
|
99
|
+
default: undefined
|
|
100
|
+
},
|
|
101
|
+
placement: {
|
|
102
|
+
type: String as PropType<Placement>,
|
|
103
|
+
default: 'right'
|
|
104
|
+
},
|
|
105
|
+
width: {
|
|
106
|
+
type: [String, Number],
|
|
107
|
+
default: '378px'
|
|
108
|
+
},
|
|
109
|
+
height: {
|
|
110
|
+
type: [String, Number],
|
|
111
|
+
default: '378px'
|
|
112
|
+
},
|
|
113
|
+
title: {
|
|
114
|
+
type: String,
|
|
115
|
+
default: ''
|
|
116
|
+
},
|
|
117
|
+
showClose: {
|
|
118
|
+
type: Boolean,
|
|
119
|
+
default: true
|
|
120
|
+
},
|
|
121
|
+
showMask: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: true
|
|
124
|
+
},
|
|
125
|
+
outsideClose: {
|
|
126
|
+
type: Boolean,
|
|
127
|
+
default: true
|
|
128
|
+
},
|
|
129
|
+
focusTrap: {
|
|
130
|
+
type: Boolean,
|
|
131
|
+
default: true
|
|
132
|
+
},
|
|
133
|
+
zIndex: {
|
|
134
|
+
type: Number
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
emits: ['open', 'close', 'update:open'],
|
|
138
|
+
setup(props, { emit, slots }) {
|
|
139
|
+
const { nextZIndex } = useZIndex();
|
|
140
|
+
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
141
|
+
provide(ZINDEX, zIndex);
|
|
142
|
+
|
|
143
|
+
const bodyRef = ref<HTMLElement | null>(null);
|
|
144
|
+
const hasFooter = computed(() => !!slots.footer);
|
|
145
|
+
const hasTitle = computed(() => !!slots.title || !!props.title);
|
|
146
|
+
const hasTrigger = computed(() => !!slots.trigger);
|
|
147
|
+
|
|
148
|
+
const internalOpen = ref(false);
|
|
149
|
+
|
|
150
|
+
const isOpen = computed({
|
|
151
|
+
get: () => {
|
|
152
|
+
if (props.open !== undefined) return props.open;
|
|
153
|
+
return internalOpen.value;
|
|
154
|
+
},
|
|
155
|
+
set: (value: boolean) => {
|
|
156
|
+
if (props.open !== undefined) {
|
|
157
|
+
emit('update:open', value);
|
|
158
|
+
} else {
|
|
159
|
+
internalOpen.value = value;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const isHorizontal = computed(() =>
|
|
165
|
+
props.placement === 'left' || props.placement === 'right'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const contentStyle = computed<CSSProperties>(() => {
|
|
169
|
+
const s: CSSProperties = { zIndex: zIndex.value };
|
|
170
|
+
if (isHorizontal.value) {
|
|
171
|
+
const w = props.width;
|
|
172
|
+
s.width = typeof w === 'number' ? `${w}px` : w;
|
|
173
|
+
} else {
|
|
174
|
+
const h = props.height;
|
|
175
|
+
s.height = typeof h === 'number' ? `${h}px` : h;
|
|
176
|
+
}
|
|
177
|
+
return s;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const currentTransition = computed(() => style.slideTransition[props.placement]);
|
|
181
|
+
|
|
182
|
+
const focusableSelector = [
|
|
183
|
+
'[autofocus]',
|
|
184
|
+
'input:not([type="hidden"])',
|
|
185
|
+
'textarea',
|
|
186
|
+
'select',
|
|
187
|
+
'[contenteditable="true"]',
|
|
188
|
+
'button',
|
|
189
|
+
'a[href]',
|
|
190
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
191
|
+
].join(', ');
|
|
192
|
+
|
|
193
|
+
function getFirstFocusable() {
|
|
194
|
+
if (!bodyRef.value) return null;
|
|
195
|
+
return Array.from(
|
|
196
|
+
bodyRef.value.querySelectorAll<HTMLElement>(focusableSelector)
|
|
197
|
+
).find(el =>
|
|
198
|
+
!el.matches(':disabled') &&
|
|
199
|
+
el.getAttribute('aria-disabled') !== 'true' &&
|
|
200
|
+
el.getAttribute('aria-hidden') !== 'true' &&
|
|
201
|
+
el.getClientRects().length > 0
|
|
202
|
+
) ?? null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const handleOpenAutoFocus = (e: Event) => {
|
|
206
|
+
const first = getFirstFocusable();
|
|
207
|
+
if (first) {
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
first.focus();
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const close = (e: MouseEvent | KeyboardEvent | undefined) => {
|
|
214
|
+
emit('close', e);
|
|
215
|
+
isOpen.value = false;
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const onEscapeKeyDown = (e: KeyboardEvent) => {
|
|
219
|
+
e.stopPropagation();
|
|
220
|
+
close(e);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const onPointerDownOutside = (e: Event) => {
|
|
224
|
+
if (!props.outsideClose) e.preventDefault();
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const onInteractOutside = (e: Event) => {
|
|
228
|
+
if (!props.outsideClose) {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
close(e as MouseEvent | KeyboardEvent);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const handleOpenChange = (value: boolean) => {
|
|
236
|
+
if (!value && isOpen.value) {
|
|
237
|
+
close(undefined);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
isOpen.value = value;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
watch(
|
|
244
|
+
() => isOpen.value,
|
|
245
|
+
(next, prev) => {
|
|
246
|
+
if (next && !prev) {
|
|
247
|
+
zIndex.value = props.zIndex ?? nextZIndex();
|
|
248
|
+
emit('open');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const open = () => { isOpen.value = true; };
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
style,
|
|
257
|
+
fade,
|
|
258
|
+
isOpen,
|
|
259
|
+
zIndex,
|
|
260
|
+
hasFooter,
|
|
261
|
+
hasTitle,
|
|
262
|
+
hasTrigger,
|
|
263
|
+
bodyRef,
|
|
264
|
+
contentStyle,
|
|
265
|
+
currentTransition,
|
|
266
|
+
open,
|
|
267
|
+
close,
|
|
268
|
+
handleOpenAutoFocus,
|
|
269
|
+
handleOpenChange,
|
|
270
|
+
onEscapeKeyDown,
|
|
271
|
+
onPointerDownOutside,
|
|
272
|
+
onInteractOutside
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer ref="drawer" title="Basic Drawer">
|
|
3
|
+
<p style="margin: 0">This is the drawer body. It slides in from the right by default.</p>
|
|
4
|
+
</MDrawer>
|
|
5
|
+
|
|
6
|
+
<MButton @click="drawer?.open()">Open Drawer</MButton>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script lang="ts" setup>
|
|
10
|
+
import { ref } from 'vue';
|
|
11
|
+
import MDrawer from '../Drawer.vue';
|
|
12
|
+
import MButton from '../../button/Button.vue';
|
|
13
|
+
|
|
14
|
+
const drawer = ref<InstanceType<typeof MDrawer> | null>(null);
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer ref="drawer">
|
|
3
|
+
<template #title>
|
|
4
|
+
<span style="display: flex; align-items: center; gap: 6px">
|
|
5
|
+
<span style="font-size: 1.1em">⚙️</span>
|
|
6
|
+
Settings
|
|
7
|
+
</span>
|
|
8
|
+
</template>
|
|
9
|
+
<p style="margin: 0">Custom title slot with an icon.</p>
|
|
10
|
+
</MDrawer>
|
|
11
|
+
|
|
12
|
+
<MButton @click="drawer?.open()">Open Drawer</MButton>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script lang="ts" setup>
|
|
16
|
+
import { ref } from 'vue';
|
|
17
|
+
import MDrawer from '../Drawer.vue';
|
|
18
|
+
import MButton from '../../button/Button.vue';
|
|
19
|
+
|
|
20
|
+
const drawer = ref<InstanceType<typeof MDrawer> | null>(null);
|
|
21
|
+
</script>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import MDrawer from '../Drawer.vue';
|
|
2
|
+
import {
|
|
3
|
+
MDrawerBasic,
|
|
4
|
+
MDrawerPlacements,
|
|
5
|
+
MDrawerNoMask,
|
|
6
|
+
MDrawerWithFooter,
|
|
7
|
+
MDrawerCustomTitle,
|
|
8
|
+
MDrawerWithTrigger,
|
|
9
|
+
MDrawerNoOutsideClose
|
|
10
|
+
} from './index';
|
|
11
|
+
import { createStory } from '../../../__storybook__';
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
component: MDrawer,
|
|
15
|
+
title: 'Components/Drawer'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const Basic = createStory(MDrawerBasic);
|
|
19
|
+
export const WithTrigger = createStory(MDrawerWithTrigger);
|
|
20
|
+
export const Placements = createStory(MDrawerPlacements);
|
|
21
|
+
export const NoMask = createStory(MDrawerNoMask);
|
|
22
|
+
export const WithFooter = createStory(MDrawerWithFooter);
|
|
23
|
+
export const CustomTitle = createStory(MDrawerCustomTitle);
|
|
24
|
+
export const NoOutsideClose = createStory(MDrawerNoOutsideClose);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as MDrawerBasic } from './basic.vue';
|
|
2
|
+
export { default as MDrawerPlacements } from './placements.vue';
|
|
3
|
+
export { default as MDrawerNoMask } from './no-mask.vue';
|
|
4
|
+
export { default as MDrawerWithFooter } from './with-footer.vue';
|
|
5
|
+
export { default as MDrawerCustomTitle } from './custom-title.vue';
|
|
6
|
+
export { default as MDrawerWithTrigger } from './with-trigger.vue';
|
|
7
|
+
export { default as MDrawerNoOutsideClose } from './no-outside-close.vue';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer v-model:open="isOpen" title="No Backdrop" :show-mask="false">
|
|
3
|
+
<p style="margin: 0">This drawer has no backdrop overlay.</p>
|
|
4
|
+
</MDrawer>
|
|
5
|
+
|
|
6
|
+
<MButton @click="isOpen = true">Open (no backdrop)</MButton>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script lang="ts" setup>
|
|
10
|
+
import { ref } from 'vue';
|
|
11
|
+
import MDrawer from '../Drawer.vue';
|
|
12
|
+
import MButton from '../../button/Button.vue';
|
|
13
|
+
|
|
14
|
+
const isOpen = ref(false);
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer ref="drawer" title="Persistent Drawer" :outside-close="false">
|
|
3
|
+
<p style="margin: 0">Clicking outside won't close this drawer. Use the × button or Escape.</p>
|
|
4
|
+
</MDrawer>
|
|
5
|
+
|
|
6
|
+
<MButton @click="drawer?.open()">Open Drawer</MButton>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script lang="ts" setup>
|
|
10
|
+
import { ref } from 'vue';
|
|
11
|
+
import MDrawer from '../Drawer.vue';
|
|
12
|
+
import MButton from '../../button/Button.vue';
|
|
13
|
+
|
|
14
|
+
const drawer = ref<InstanceType<typeof MDrawer> | null>(null);
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer v-model:open="isOpen" :placement="activePlacement" :title="`Drawer — ${activePlacement}`">
|
|
3
|
+
<p style="margin: 0">Sliding in from the <strong>{{ activePlacement }}</strong>.</p>
|
|
4
|
+
</MDrawer>
|
|
5
|
+
|
|
6
|
+
<div style="display: flex; gap: 8px; flex-wrap: wrap">
|
|
7
|
+
<MButton @click="open('left')">Left</MButton>
|
|
8
|
+
<MButton @click="open('right')">Right</MButton>
|
|
9
|
+
<MButton @click="open('top')">Top</MButton>
|
|
10
|
+
<MButton @click="open('bottom')">Bottom</MButton>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script lang="ts" setup>
|
|
15
|
+
import { ref } from 'vue';
|
|
16
|
+
import MDrawer from '../Drawer.vue';
|
|
17
|
+
import MButton from '../../button/Button.vue';
|
|
18
|
+
|
|
19
|
+
type Placement = 'left' | 'right' | 'top' | 'bottom';
|
|
20
|
+
|
|
21
|
+
const isOpen = ref(false);
|
|
22
|
+
const activePlacement = ref<Placement>('right');
|
|
23
|
+
|
|
24
|
+
function open(placement: Placement) {
|
|
25
|
+
activePlacement.value = placement;
|
|
26
|
+
isOpen.value = true;
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer v-model:open="isOpen" title="Drawer with Footer">
|
|
3
|
+
<p style="margin: 0">Drawer body content goes here.</p>
|
|
4
|
+
<template #footer>
|
|
5
|
+
<div style="display: flex; gap: 8px; justify-content: flex-end; width: 100%">
|
|
6
|
+
<MButton @click="isOpen = false">Cancel</MButton>
|
|
7
|
+
<MButton variant="primary" @click="isOpen = false">Save</MButton>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
</MDrawer>
|
|
11
|
+
|
|
12
|
+
<MButton @click="isOpen = true">Open Drawer</MButton>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script lang="ts" setup>
|
|
16
|
+
import { ref } from 'vue';
|
|
17
|
+
import MDrawer from '../Drawer.vue';
|
|
18
|
+
import MButton from '../../button/Button.vue';
|
|
19
|
+
|
|
20
|
+
const isOpen = ref(false);
|
|
21
|
+
</script>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<MDrawer title="Profile">
|
|
3
|
+
<template #trigger>
|
|
4
|
+
<MButton>Edit profile</MButton>
|
|
5
|
+
</template>
|
|
6
|
+
<p style="margin: 0">Trigger slot — no ref or v-model needed.</p>
|
|
7
|
+
</MDrawer>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script lang="ts" setup>
|
|
11
|
+
import MDrawer from '../Drawer.vue';
|
|
12
|
+
import MButton from '../../button/Button.vue';
|
|
13
|
+
</script>
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
globalKeyframes,
|
|
3
|
+
style,
|
|
4
|
+
styleVariants
|
|
5
|
+
} from '@vanilla-extract/css';
|
|
6
|
+
import { themeVars } from '../../../styles/theme.css';
|
|
7
|
+
|
|
8
|
+
export const mask = style({
|
|
9
|
+
position: 'fixed',
|
|
10
|
+
top: 0,
|
|
11
|
+
insetInlineEnd: 0,
|
|
12
|
+
bottom: 0,
|
|
13
|
+
insetInlineStart: 0,
|
|
14
|
+
height: '100%',
|
|
15
|
+
backgroundColor: 'hsla(0,0%,45.1%,0.7)'
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const drawerBase = {
|
|
19
|
+
position: 'fixed' as const,
|
|
20
|
+
backgroundColor: '#fff',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
flexDirection: 'column' as const,
|
|
23
|
+
boxShadow:
|
|
24
|
+
'0 6px 16px 0 rgba(0,0,0,0.08), 0 3px 6px -4px rgba(0,0,0,0.12), 0 9px 28px 8px rgba(0,0,0,0.05)',
|
|
25
|
+
selectors: {
|
|
26
|
+
'&:focus': { outline: 'none' },
|
|
27
|
+
'&:focus-visible': { outline: 'none' }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const placement = styleVariants({
|
|
32
|
+
left: {
|
|
33
|
+
...drawerBase,
|
|
34
|
+
top: 0,
|
|
35
|
+
left: 0,
|
|
36
|
+
bottom: 0,
|
|
37
|
+
height: '100%'
|
|
38
|
+
},
|
|
39
|
+
right: {
|
|
40
|
+
...drawerBase,
|
|
41
|
+
top: 0,
|
|
42
|
+
right: 0,
|
|
43
|
+
bottom: 0,
|
|
44
|
+
height: '100%'
|
|
45
|
+
},
|
|
46
|
+
top: {
|
|
47
|
+
...drawerBase,
|
|
48
|
+
top: 0,
|
|
49
|
+
left: 0,
|
|
50
|
+
right: 0,
|
|
51
|
+
width: '100%'
|
|
52
|
+
},
|
|
53
|
+
bottom: {
|
|
54
|
+
...drawerBase,
|
|
55
|
+
bottom: 0,
|
|
56
|
+
left: 0,
|
|
57
|
+
right: 0,
|
|
58
|
+
width: '100%'
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const DURATION = '0.28s';
|
|
63
|
+
const TIMING = 'cubic-bezier(0.16, 1, 0.3, 1)';
|
|
64
|
+
|
|
65
|
+
const SLIDE_LEFT_IN = 'drawer-slide-left-in';
|
|
66
|
+
const SLIDE_LEFT_OUT = 'drawer-slide-left-out';
|
|
67
|
+
const SLIDE_RIGHT_IN = 'drawer-slide-right-in';
|
|
68
|
+
const SLIDE_RIGHT_OUT = 'drawer-slide-right-out';
|
|
69
|
+
const SLIDE_TOP_IN = 'drawer-slide-top-in';
|
|
70
|
+
const SLIDE_TOP_OUT = 'drawer-slide-top-out';
|
|
71
|
+
const SLIDE_BOTTOM_IN = 'drawer-slide-bottom-in';
|
|
72
|
+
const SLIDE_BOTTOM_OUT = 'drawer-slide-bottom-out';
|
|
73
|
+
|
|
74
|
+
globalKeyframes(SLIDE_LEFT_IN, {
|
|
75
|
+
from: { transform: 'translateX(-100%)' },
|
|
76
|
+
to: { transform: 'translateX(0)' }
|
|
77
|
+
});
|
|
78
|
+
globalKeyframes(SLIDE_LEFT_OUT, {
|
|
79
|
+
from: { transform: 'translateX(0)' },
|
|
80
|
+
to: { transform: 'translateX(-100%)' }
|
|
81
|
+
});
|
|
82
|
+
globalKeyframes(SLIDE_RIGHT_IN, {
|
|
83
|
+
from: { transform: 'translateX(100%)' },
|
|
84
|
+
to: { transform: 'translateX(0)' }
|
|
85
|
+
});
|
|
86
|
+
globalKeyframes(SLIDE_RIGHT_OUT, {
|
|
87
|
+
from: { transform: 'translateX(0)' },
|
|
88
|
+
to: { transform: 'translateX(100%)' }
|
|
89
|
+
});
|
|
90
|
+
globalKeyframes(SLIDE_TOP_IN, {
|
|
91
|
+
from: { transform: 'translateY(-100%)' },
|
|
92
|
+
to: { transform: 'translateY(0)' }
|
|
93
|
+
});
|
|
94
|
+
globalKeyframes(SLIDE_TOP_OUT, {
|
|
95
|
+
from: { transform: 'translateY(0)' },
|
|
96
|
+
to: { transform: 'translateY(-100%)' }
|
|
97
|
+
});
|
|
98
|
+
globalKeyframes(SLIDE_BOTTOM_IN, {
|
|
99
|
+
from: { transform: 'translateY(100%)' },
|
|
100
|
+
to: { transform: 'translateY(0)' }
|
|
101
|
+
});
|
|
102
|
+
globalKeyframes(SLIDE_BOTTOM_OUT, {
|
|
103
|
+
from: { transform: 'translateY(0)' },
|
|
104
|
+
to: { transform: 'translateY(100%)' }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const slideTransition = {
|
|
108
|
+
left: {
|
|
109
|
+
enterActive: style({ animation: `${DURATION} ${SLIDE_LEFT_IN} ${TIMING}`, willChange: 'transform' }),
|
|
110
|
+
leaveActive: style({ animation: `${DURATION} ${SLIDE_LEFT_OUT} ${TIMING}`, willChange: 'transform' }),
|
|
111
|
+
enterFrom: style({ transform: 'translateX(-100%)' }),
|
|
112
|
+
leaveTo: style({ transform: 'translateX(-100%)' })
|
|
113
|
+
},
|
|
114
|
+
right: {
|
|
115
|
+
enterActive: style({ animation: `${DURATION} ${SLIDE_RIGHT_IN} ${TIMING}`, willChange: 'transform' }),
|
|
116
|
+
leaveActive: style({ animation: `${DURATION} ${SLIDE_RIGHT_OUT} ${TIMING}`, willChange: 'transform' }),
|
|
117
|
+
enterFrom: style({ transform: 'translateX(100%)' }),
|
|
118
|
+
leaveTo: style({ transform: 'translateX(100%)' })
|
|
119
|
+
},
|
|
120
|
+
top: {
|
|
121
|
+
enterActive: style({ animation: `${DURATION} ${SLIDE_TOP_IN} ${TIMING}`, willChange: 'transform' }),
|
|
122
|
+
leaveActive: style({ animation: `${DURATION} ${SLIDE_TOP_OUT} ${TIMING}`, willChange: 'transform' }),
|
|
123
|
+
enterFrom: style({ transform: 'translateY(-100%)' }),
|
|
124
|
+
leaveTo: style({ transform: 'translateY(-100%)' })
|
|
125
|
+
},
|
|
126
|
+
bottom: {
|
|
127
|
+
enterActive: style({ animation: `${DURATION} ${SLIDE_BOTTOM_IN} ${TIMING}`, willChange: 'transform' }),
|
|
128
|
+
leaveActive: style({ animation: `${DURATION} ${SLIDE_BOTTOM_OUT} ${TIMING}`, willChange: 'transform' }),
|
|
129
|
+
enterFrom: style({ transform: 'translateY(100%)' }),
|
|
130
|
+
leaveTo: style({ transform: 'translateY(100%)' })
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const header = style({
|
|
135
|
+
display: 'flex',
|
|
136
|
+
justifyContent: 'space-between',
|
|
137
|
+
alignItems: 'center',
|
|
138
|
+
padding: `${themeVars.space[4]} ${themeVars.space[4]} 0 ${themeVars.space[4]}`,
|
|
139
|
+
flexShrink: '0'
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
export const title = style({
|
|
143
|
+
margin: 0,
|
|
144
|
+
fontSize: `${themeVars.fontSize.xlarge}`,
|
|
145
|
+
fontWeight: '500',
|
|
146
|
+
color: themeVars.color.font.default
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export const close = style({
|
|
150
|
+
outline: 'none',
|
|
151
|
+
display: 'inline-flex',
|
|
152
|
+
alignItems: 'center',
|
|
153
|
+
justifyContent: 'center',
|
|
154
|
+
width: '1.5rem',
|
|
155
|
+
height: '1.5rem',
|
|
156
|
+
backgroundColor: 'transparent',
|
|
157
|
+
border: 'none',
|
|
158
|
+
padding: 0,
|
|
159
|
+
cursor: 'pointer',
|
|
160
|
+
borderRadius: themeVars.rounded.full,
|
|
161
|
+
position: 'relative',
|
|
162
|
+
transition: 'background-color 0.2s',
|
|
163
|
+
':hover': {
|
|
164
|
+
backgroundColor: themeVars.color.gray[150]
|
|
165
|
+
},
|
|
166
|
+
':focus-visible': {
|
|
167
|
+
outline: 'none',
|
|
168
|
+
backgroundColor: themeVars.color.gray[150]
|
|
169
|
+
},
|
|
170
|
+
'::before': {
|
|
171
|
+
content: '""',
|
|
172
|
+
position: 'absolute',
|
|
173
|
+
top: '50%',
|
|
174
|
+
left: '50%',
|
|
175
|
+
transform: 'translate(-50%, -50%) rotate(45deg)',
|
|
176
|
+
width: '0.8rem',
|
|
177
|
+
height: '2px',
|
|
178
|
+
backgroundColor: themeVars.color.gray[500],
|
|
179
|
+
transformOrigin: 'center'
|
|
180
|
+
},
|
|
181
|
+
'::after': {
|
|
182
|
+
content: '""',
|
|
183
|
+
position: 'absolute',
|
|
184
|
+
top: '50%',
|
|
185
|
+
left: '50%',
|
|
186
|
+
transform: 'translate(-50%, -50%) rotate(-45deg)',
|
|
187
|
+
width: '0.8rem',
|
|
188
|
+
height: '2px',
|
|
189
|
+
backgroundColor: themeVars.color.gray[500],
|
|
190
|
+
transformOrigin: 'center'
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export const body = style({
|
|
195
|
+
padding: themeVars.space[4],
|
|
196
|
+
display: 'flex',
|
|
197
|
+
flexDirection: 'column',
|
|
198
|
+
flexGrow: '1',
|
|
199
|
+
overflowY: 'auto',
|
|
200
|
+
color: themeVars.color.font.text
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
export const footer = style({
|
|
204
|
+
display: 'flex',
|
|
205
|
+
alignItems: 'center',
|
|
206
|
+
padding: `0 ${themeVars.space[4]} ${themeVars.space[4]} ${themeVars.space[4]}`,
|
|
207
|
+
flexShrink: '0'
|
|
208
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Drawer.css';
|
package/src/components/index.ts
CHANGED
|
@@ -55,5 +55,6 @@ export { default as MConfirm } from './confirm/Confirm.vue';
|
|
|
55
55
|
export { default as MConfirmProvider } from './confirm/ConfirmProvider.vue';
|
|
56
56
|
export { default as MDialog } from './dialog/Dialog.vue';
|
|
57
57
|
export { default as MDialogProvider } from './dialogprovider/DialogProvider.vue';
|
|
58
|
+
export { default as MDrawer } from './drawer/Drawer.vue';
|
|
58
59
|
export { default as MTree } from './tree/Tree.vue';
|
|
59
60
|
export { default as MPopover } from './popover/Popover.vue';
|