Skip to content

Commit 0a39cf6

Browse files
authored
Transition component (#326)
* add redent function when verifying snapshots This allows us not to care about the correct amount of spaces and always produces a clean output. * make the container the parent of the wrapper element * drop the visible prop on the Portal component * drop visible prop on Portal component + Also cleanup a little bit * expose the RenderStrategy * implement Transition component in Vue * expose Transition component * add Transitions to the Dialog example
1 parent 6fa6c45 commit 0a39cf6

File tree

21 files changed

+3057
-409
lines changed

21 files changed

+3057
-409
lines changed

packages/@headlessui-react/src/components/transitions/transition.test.tsx

Lines changed: 193 additions & 193 deletions
Large diffs are not rendered by default.

packages/@headlessui-react/src/test-utils/execute-timeline.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ import { render } from '@testing-library/react'
44
import { disposables } from '../utils/disposables'
55
import { reportChanges } from './report-dom-node-changes'
66

7+
function redentSnapshot(input: string) {
8+
let minSpaces = Infinity
9+
let lines = input.split('\n')
10+
for (let line of lines) {
11+
if (line.trim() === '---') continue
12+
let spacesInLine = (line.match(/^[+-](\s+)/g) || []).pop()!.length - 1
13+
minSpaces = Math.min(minSpaces, spacesInLine)
14+
}
15+
16+
let replacer = new RegExp(`^([+-])\\s{${minSpaces}}(.*)`, 'g')
17+
18+
return input
19+
.split('\n')
20+
.map(line =>
21+
line.trim() === '---' ? line : line.replace(replacer, (_, sign, rest) => `${sign} ${rest}`)
22+
)
23+
.join('\n')
24+
}
25+
726
export async function executeTimeline(
827
element: JSX.Element,
928
steps: ((tools: ReturnType<typeof render>) => (null | number)[])[]
@@ -95,16 +114,18 @@ export async function executeTimeline(
95114
? 'yes'
96115
: `no, it took ${call.relativeToPreviousSnapshot}ms`
97116
})`
98-
}\n${snapshotDiff(uniqueSnapshots[i - 1].content, call.content, {
99-
aAnnotation: '__REMOVE_ME__',
100-
bAnnotation: '__REMOVE_ME__',
101-
contextLines: 0,
102-
})
103-
// Just to do some cleanup
104-
.replace(/\n\n@@([^@@]*)@@/g, '') // Top level @@ signs
105-
.replace(/@@([^@@]*)@@/g, '---') // In between @@ signs
106-
.replace(/[-+] __REMOVE_ME__\n/g, '')
107-
.replace(/Snapshot Diff:\n/g, '')
117+
}\n${redentSnapshot(
118+
snapshotDiff(uniqueSnapshots[i - 1].content, call.content, {
119+
aAnnotation: '__REMOVE_ME__',
120+
bAnnotation: '__REMOVE_ME__',
121+
contextLines: 0,
122+
})
123+
// Just to do some cleanup
124+
.replace(/\n\n@@([^@@]*)@@/g, '') // Top level @@ signs
125+
.replace(/@@([^@@]*)@@/g, '---') // In between @@ signs
126+
.replace(/[-+] __REMOVE_ME__\n/g, '')
127+
.replace(/Snapshot Diff:\n/g, '')
128+
)
108129
.split('\n')
109130
.map(line => ` ${line}`)
110131
.join('\n')}`

packages/@headlessui-vue/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ _This project is still in early development. New components will be added regula
3838
- [Dialog](./src/components/dialog/README.md)
3939
- [Popover](./src/components/popover/README.md)
4040
- [Radio Group](./src/components/radio-group/README.md)
41+
- [Transition](./src/components/transitions/README.md)
4142

4243
### Roadmap
4344

packages/@headlessui-vue/examples/src/components/dialog/dialog.vue

Lines changed: 149 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -7,132 +7,162 @@
77
Toggle!
88
</button>
99

10-
<Dialog :open="isOpen" :onClose="setIsOpen">
11-
<div class="fixed z-10 inset-0 overflow-y-auto">
12-
<div
13-
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
14-
>
15-
<DialogOverlay class="fixed inset-0 transition-opacity">
16-
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
17-
</DialogOverlay>
18-
19-
<!-- This element is to trick the browser into centering the modal contents. -->
20-
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
21-
&#8203;
22-
</span>
10+
<TransitionRoot :show="isOpen" as="template">
11+
<Dialog :open="isOpen" @close="setIsOpen" static>
12+
<div class="fixed z-10 inset-0 overflow-y-auto">
2313
<div
24-
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
14+
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
2515
>
26-
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
27-
<div class="sm:flex sm:items-start">
28-
<div
29-
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10"
30-
>
31-
<!-- Heroicon name: exclamation -->
32-
<svg
33-
class="h-6 w-6 text-red-600"
34-
xmlns="http://www.w3.org/2000/svg"
35-
fill="none"
36-
viewBox="0 0 24 24"
37-
stroke="currentColor"
38-
aria-hidden="true"
39-
>
40-
<path
41-
strokeLinecap="round"
42-
strokeLinejoin="round"
43-
stroke-width="2"
44-
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
45-
/>
46-
</svg>
47-
</div>
48-
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
49-
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-900">
50-
Deactivate account
51-
</DialogTitle>
52-
<div class="mt-2">
53-
<p class="text-sm text-gray-500">
54-
Are you sure you want to deactivate your account? All of your data will be
55-
permanently removed. This action cannot be undone.
56-
</p>
57-
<div class="relative inline-block text-left mt-10">
58-
<Menu v-slot="{ open }">
59-
<span class="rounded-md shadow-sm">
60-
<MenuButton
61-
ref="trigger"
62-
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
63-
>
64-
<span>Choose a reason</span>
65-
<svg class="w-5 h-5 ml-2 -mr-1" viewBox="0 0 20 20" fill="currentColor">
66-
<path
67-
fillRule="evenodd"
68-
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
69-
clipRule="evenodd"
70-
/>
71-
</svg>
72-
</MenuButton>
73-
</span>
16+
<TransitionChild
17+
as="template"
18+
enter="ease-out duration-300"
19+
enterFrom="opacity-0"
20+
enterTo="opacity-100"
21+
leave="ease-in duration-200"
22+
leaveFrom="opacity-100"
23+
leaveTo="opacity-0"
24+
>
25+
<DialogOverlay class="fixed inset-0 transition-opacity">
26+
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
27+
</DialogOverlay>
28+
</TransitionChild>
29+
30+
<TransitionChild
31+
enter="ease-out transform duration-300"
32+
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
33+
enterTo="opacity-100 translate-y-0 sm:scale-100"
34+
leave="ease-in transform duration-200"
35+
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
36+
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
37+
>
38+
<!-- This element is to trick the browser into centering the modal contents. -->
39+
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
40+
&#8203;
41+
</span>
42+
<div
43+
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
44+
>
45+
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
46+
<div class="sm:flex sm:items-start">
47+
<div
48+
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10"
49+
>
50+
<!-- Heroicon name: exclamation -->
51+
<svg
52+
class="h-6 w-6 text-red-600"
53+
xmlns="http://www.w3.org/2000/svg"
54+
fill="none"
55+
viewBox="0 0 24 24"
56+
stroke="currentColor"
57+
aria-hidden="true"
58+
>
59+
<path
60+
strokeLinecap="round"
61+
strokeLinejoin="round"
62+
stroke-width="2"
63+
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
64+
/>
65+
</svg>
66+
</div>
67+
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
68+
<DialogTitle as="h3" class="text-lg leading-6 font-medium text-gray-900">
69+
Deactivate account
70+
</DialogTitle>
71+
<div class="mt-2">
72+
<p class="text-sm text-gray-500">
73+
Are you sure you want to deactivate your account? All of your data will be
74+
permanently removed. This action cannot be undone.
75+
</p>
76+
<div class="relative inline-block text-left mt-10">
77+
<Menu v-slot="{ open }">
78+
<span class="rounded-md shadow-sm">
79+
<MenuButton
80+
ref="trigger"
81+
class="inline-flex justify-center w-full px-4 py-2 text-sm font-medium leading-5 text-gray-700 transition duration-150 ease-in-out bg-white border border-gray-300 rounded-md hover:text-gray-500 focus:outline-none focus:border-blue-300 focus:shadow-outline-blue active:bg-gray-50 active:text-gray-800"
82+
>
83+
<span>Choose a reason</span>
84+
<svg
85+
class="w-5 h-5 ml-2 -mr-1"
86+
viewBox="0 0 20 20"
87+
fill="currentColor"
88+
>
89+
<path
90+
fillRule="evenodd"
91+
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
92+
clipRule="evenodd"
93+
/>
94+
</svg>
95+
</MenuButton>
96+
</span>
7497

75-
<Portal v-if="open">
76-
<MenuItems
77-
static
78-
ref="container"
79-
class="z-20 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
80-
>
81-
<div class="px-4 py-3">
82-
<p class="text-sm leading-5">Signed in as</p>
83-
<p class="text-sm font-medium leading-5 text-gray-900 truncate">
84-
85-
</p>
86-
</div>
98+
<Portal v-if="open">
99+
<MenuItems
100+
static
101+
ref="container"
102+
class="z-20 w-56 mt-2 origin-top-right bg-white border border-gray-200 divide-y divide-gray-100 rounded-md shadow-lg outline-none"
103+
>
104+
<div class="px-4 py-3">
105+
<p class="text-sm leading-5">Signed in as</p>
106+
<p class="text-sm font-medium leading-5 text-gray-900 truncate">
107+
108+
</p>
109+
</div>
87110

88-
<div class="py-1">
89-
<MenuItem as="a" href="#account-settings" :className="resolveClass">
90-
Account settings
91-
</MenuItem>
92-
<MenuItem as="a" href="#support" :className="resolveClass">
93-
Support
94-
</MenuItem>
95-
<MenuItem as="a" disabled href="#new-feature" :className="resolveClass">
96-
New feature (soon)
97-
</MenuItem>
98-
<MenuItem as="a" href="#license" :className="resolveClass">
99-
License
100-
</MenuItem>
101-
</div>
111+
<div class="py-1">
112+
<MenuItem as="a" href="#account-settings" :className="resolveClass">
113+
Account settings
114+
</MenuItem>
115+
<MenuItem as="a" href="#support" :className="resolveClass">
116+
Support
117+
</MenuItem>
118+
<MenuItem
119+
as="a"
120+
disabled
121+
href="#new-feature"
122+
:className="resolveClass"
123+
>
124+
New feature (soon)
125+
</MenuItem>
126+
<MenuItem as="a" href="#license" :className="resolveClass">
127+
License
128+
</MenuItem>
129+
</div>
102130

103-
<div class="py-1">
104-
<MenuItem as="a" href="#sign-out" :className="resolveClass">
105-
Sign out
106-
</MenuItem>
107-
</div>
108-
</MenuItems>
109-
</Portal>
110-
</Menu>
131+
<div class="py-1">
132+
<MenuItem as="a" href="#sign-out" :className="resolveClass">
133+
Sign out
134+
</MenuItem>
135+
</div>
136+
</MenuItems>
137+
</Portal>
138+
</Menu>
139+
</div>
140+
</div>
111141
</div>
112142
</div>
113143
</div>
144+
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
145+
<button
146+
type="button"
147+
@click="setIsOpen(false)"
148+
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:shadow-outline-red sm:ml-3 sm:w-auto sm:text-sm"
149+
>
150+
Deactivate
151+
</button>
152+
<button
153+
type="button"
154+
@click="setIsOpen(false)"
155+
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:shadow-outline-indigo sm:mt-0 sm:w-auto sm:text-sm"
156+
>
157+
Cancel
158+
</button>
159+
</div>
114160
</div>
115-
</div>
116-
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
117-
<button
118-
type="button"
119-
@click="setIsOpen(false)"
120-
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:shadow-outline-red sm:ml-3 sm:w-auto sm:text-sm"
121-
>
122-
Deactivate
123-
</button>
124-
<button
125-
type="button"
126-
@click="setIsOpen(false)"
127-
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:shadow-outline-indigo sm:mt-0 sm:w-auto sm:text-sm"
128-
>
129-
Cancel
130-
</button>
131-
</div>
161+
</TransitionChild>
132162
</div>
133163
</div>
134-
</div>
135-
</Dialog>
164+
</Dialog>
165+
</TransitionRoot>
136166
</template>
137167

138168
<script>
@@ -146,6 +176,8 @@ import {
146176
MenuItems,
147177
MenuItem,
148178
Portal,
179+
Transition,
180+
TransitionChild,
149181
} from '@headlessui/vue'
150182
import { usePopper } from '../../playground-utils/hooks/use-popper'
151183
@@ -171,6 +203,8 @@ export default {
171203
MenuItems,
172204
MenuItem,
173205
Portal,
206+
TransitionRoot: Transition,
207+
TransitionChild,
174208
},
175209
setup() {
176210
let isOpen = ref(false)

0 commit comments

Comments
 (0)