Skip to content

Commit f2566d9

Browse files
committedMar 18, 2025·
chore: wip
1 parent 99515fd commit f2566d9

File tree

5 files changed

+136
-48
lines changed

5 files changed

+136
-48
lines changed
 

‎storage/framework/core/components/dialog/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"files": ["README.md", "dist"],
2929
"scripts": {
3030
"dev": "bunx --bun vite -c ./vite.config.ts",
31-
"build": "bunx --bun vite build --mode lib",
31+
"build": "bunx --bun vite build --mode lib && bun run build:types",
3232
"build:demo": "bunx --bun vite build",
3333
"build:types": "bunx vue-tsc -p tsconfig.build.json && bunx api-extractor run",
3434
"preview": "bunx --bun vite preview"

‎storage/framework/core/components/dialog/src/App.vue

+5-5
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ function handleClose() {
2323
</script>
2424

2525
<template>
26-
<div class="modal-wrapper bg-neutral-100/66 px-4 dark:bg-neutral-900">
27-
<div class="relative mx-auto max-w-full container sm:max-w-2xl">
28-
<header class="flex-center flex-col py-20">
26+
<div class="px-4 modal-wrapper bg-neutral-100/66 dark:bg-neutral-900">
27+
<div class="container relative max-w-full mx-auto sm:max-w-2xl">
28+
<header class="flex-col py-20 flex-center">
2929
<Hero @open="handleOpen" />
3030
</header>
3131

3232
<main
33-
class="text-primary grid grid-cols-1 gap-8 pb-20 text-xs 2xl:text-sm"
33+
class="grid grid-cols-1 gap-8 pb-20 text-xs text-primary 2xl:text-sm"
3434
>
3535
<Installation />
3636
<Usage />
3737
<Transitions />
3838
</main>
3939

4040
<Transition name="fade" appear>
41-
<Dialog v-if="visible" @close="handleClose">
41+
<Dialog v-model="visible" @close="handleClose">
4242
<DialogPanel>
4343
<h2>Greetings! This is a dialog.</h2>
4444
</DialogPanel>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
<script lang="ts" setup>
2-
import { defineCustomElement, onMounted, onUnmounted } from 'vue'
2+
import { onMounted, onUnmounted, ref, watch, nextTick } from 'vue'
3+
4+
const props = defineProps<{
5+
modelValue: boolean
6+
}>()
37
48
const emit = defineEmits<{
9+
(event: 'update:modelValue', value: boolean): void
510
(event: 'close', visible: boolean): void
611
}>()
712
13+
const dialogRef = ref<HTMLElement | null>(null)
14+
const previousActiveElement = ref<HTMLElement | null>(null)
15+
816
function handleClose() {
17+
emit('update:modelValue', false)
918
emit('close', false)
1019
}
1120
@@ -15,42 +24,111 @@ function handleEscape(event: KeyboardEvent) {
1524
}
1625
}
1726
27+
function trapFocus(event: KeyboardEvent) {
28+
if (event.key !== 'Tab') return
29+
30+
const focusableElements = dialogRef.value?.querySelectorAll(
31+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
32+
)
33+
34+
if (!focusableElements?.length) return
35+
36+
const firstFocusable = focusableElements[0] as HTMLElement
37+
const lastFocusable = focusableElements[focusableElements.length - 1] as HTMLElement
38+
39+
if (event.shiftKey) {
40+
if (document.activeElement === firstFocusable) {
41+
lastFocusable.focus()
42+
}
43+
} else {
44+
if (document.activeElement === lastFocusable) {
45+
firstFocusable.focus()
46+
}
47+
}
48+
}
49+
50+
watch(() => props.modelValue, (newValue) => {
51+
if (newValue) {
52+
previousActiveElement.value = document.activeElement as HTMLElement
53+
nextTick(() => {
54+
dialogRef.value?.focus()
55+
})
56+
} else if (previousActiveElement.value) {
57+
previousActiveElement.value.focus()
58+
}
59+
})
60+
1861
onMounted(() => {
1962
window.addEventListener('keydown', handleEscape)
63+
window.addEventListener('keydown', trapFocus)
2064
})
2165
2266
onUnmounted(() => {
2367
window.removeEventListener('keydown', handleEscape)
24-
})
25-
26-
defineCustomElement({
27-
shadow: true,
68+
window.removeEventListener('keydown', trapFocus)
2869
})
2970
</script>
3071

3172
<template>
32-
<div class="fixed inset-0 z-50 overflow-y-auto">
33-
<div
34-
class="z-20 min-h-full flex items-end justify-center bg-gray-500 bg-opacity-75 p-4 text-center transition-opacity sm:items-center sm:p-0"
35-
@click.self="emit('close', false)"
36-
>
37-
<slot />
73+
<Transition name="dialog-fade">
74+
<div v-if="modelValue" class="stacks-dialog-container">
75+
<div
76+
class="stacks-dialog-backdrop"
77+
@click.self="handleClose"
78+
>
79+
<div class="stacks-dialog-content">
80+
<slot />
81+
</div>
82+
</div>
3883
</div>
39-
</div>
84+
</Transition>
4085
</template>
4186

4287
<style scoped>
43-
button {
44-
cursor: pointer;
88+
.stacks-dialog-container {
89+
position: fixed !important;
90+
inset: 0 !important;
91+
z-index: 50 !important;
92+
min-height: 100% !important;
93+
overflow-y: auto !important;
4594
}
4695
47-
.fade-enter-active,
48-
.fade-leave-active {
49-
transition: opacity 0.3s ease;
96+
.stacks-dialog-backdrop {
97+
position: fixed !important;
98+
inset: 0 !important;
99+
z-index: 20 !important;
100+
display: flex !important;
101+
align-items: flex-end !important;
102+
justify-content: center !important;
103+
min-height: 100% !important;
104+
padding: 1rem !important;
105+
text-align: center !important;
106+
transition-property: opacity !important;
107+
background-color: rgb(107 114 128 / 0.75) !important;
50108
}
51-
.fade-enter-from,
52-
.fade-leave-to {
53-
opacity: 0;
109+
110+
.stacks-dialog-content {
111+
position: relative !important;
112+
width: 100% !important;
113+
display: flex !important;
114+
align-items: center !important;
115+
justify-content: center !important;
116+
}
117+
118+
@media (min-width: 640px) {
119+
.stacks-dialog-backdrop {
120+
align-items: center !important;
121+
padding: 0 !important;
122+
}
123+
}
124+
125+
.dialog-fade-enter-active,
126+
.dialog-fade-leave-active {
127+
transition: opacity 0.3s ease !important;
128+
}
129+
130+
.dialog-fade-enter-from,
131+
.dialog-fade-leave-to {
132+
opacity: 0 !important;
54133
}
55-
/* @unocss-placeholder */
56134
</style>

‎storage/framework/core/components/dialog/src/components/DialogPanel.vue

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
<script setup lang="ts">
2+
defineProps<{
3+
class?: string
4+
}>()
5+
</script>
6+
17
<template>
2-
<div class="z-60 overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl sm:my-8 sm:max-w-lg sm:w-full sm:p-6" @click.stop>
8+
<div class="p-4 bg-white rounded-lg" @click.stop>
39
<slot>
410
<p>Default modal content goes here.</p>
511
</slot>
612
</div>
713
</template>
8-
9-
<style scoped>
10-
/* @unocss-placeholder */
11-
</style>

‎storage/framework/core/components/dialog/vite.config.ts

+24-16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { UserConfig } from 'vite'
22
import { resolve } from 'node:path'
33
import { alias } from '@stacksjs/alias'
4+
5+
import { path as p } from '@stacksjs/path'
46
import Vue from '@vitejs/plugin-vue'
57
import CleanCSS from 'clean-css'
68
import UnoCSS from 'unocss/vite'
@@ -9,23 +11,26 @@ import Icons from 'unplugin-icons/vite'
911
import Components from 'unplugin-vue-components/vite'
1012
import { defineConfig } from 'vite'
1113

12-
const cleanCssInstance = new CleanCSS({})
1314
function minify(code: string) {
15+
const cleanCssInstance = new CleanCSS({})
1416
return cleanCssInstance.minify(code).styles
1517
}
1618

17-
let cssCodeStr = ''
18-
1919
export default defineConfig(({ mode }) => {
20-
const userConfig: UserConfig = {}
20+
let cssCodeStr = ''
21+
const userConfig: UserConfig = {
22+
optimizeDeps: {
23+
exclude: ['@stacksjs/dialog'],
24+
},
25+
}
2126

2227
const commonPlugins = [
2328
Vue({
2429
include: /\.(stx|vue|md)($|\?)/,
2530
}),
26-
UnoCSS({
27-
mode: 'global',
28-
}),
31+
32+
UnoCSS(),
33+
2934
Components({
3035
extensions: ['stx', 'vue', 'md'],
3136
include: /\.(stx|vue|md)($|\?)/,
@@ -35,15 +40,16 @@ export default defineConfig(({ mode }) => {
3540
}),
3641
],
3742
}),
43+
3844
Icons(),
3945
]
4046

4147
if (mode === 'lib') {
4248
userConfig.build = {
4349
lib: {
4450
entry: resolve(__dirname, 'src/index.ts'),
45-
name: 'StacksModal',
46-
fileName: 'stacks-modal',
51+
name: 'stacks-dialog',
52+
fileName: 'index',
4753
},
4854
outDir: 'dist',
4955
emptyOutDir: true,
@@ -52,10 +58,6 @@ export default defineConfig(({ mode }) => {
5258
rollupOptions: {
5359
external: ['vue'],
5460
output: [
55-
// {
56-
// format: 'cjs',
57-
// entryFileNames: `stacks-modal.cjs`,
58-
// },
5961
{
6062
format: 'es',
6163
entryFileNames: `index.js`,
@@ -86,15 +88,15 @@ export default defineConfig(({ mode }) => {
8688

8789
return {
8890
code: `\
89-
function __insertCSSStacksModal(code) {
91+
function __insertCSSStacksDialog(code) {
9092
if (!code || typeof document == 'undefined') return
9193
let head = document.head || document.getElementsByTagName('head')[0]
9294
let style = document.createElement('style')
9395
style.type = 'text/css'
9496
head.appendChild(style)
9597
;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
9698
}\n
97-
__insertCSSStacksModal(${JSON.stringify(cssCodeStr)})
99+
__insertCSSStacksDialog(${JSON.stringify(cssCodeStr)})
98100
\n ${code}`,
99101
map: { mappings: '' },
100102
}
@@ -105,7 +107,13 @@ export default defineConfig(({ mode }) => {
105107

106108
return {
107109
resolve: {
108-
alias,
110+
'~/.env': p.projectConfigPath('env.ts'),
111+
'~/config/errors': p.projectConfigPath('errors.ts'),
112+
113+
...alias,
114+
},
115+
server: {
116+
port: 3007,
109117
},
110118
plugins: [...commonPlugins],
111119
...userConfig,

0 commit comments

Comments
 (0)
Please sign in to comment.