diff --git a/.changeset/two-jeans-share.md b/.changeset/two-jeans-share.md new file mode 100644 index 000000000..615c68da4 --- /dev/null +++ b/.changeset/two-jeans-share.md @@ -0,0 +1,5 @@ +--- +'@qwik-ui/headless': minor +--- + +We are removing the existing popover animations shimming and instead wil now only support native popover animations. This is considered a breaking change but will be more reliable overall. diff --git a/.eslintignore b/.eslintignore index 925698059..7f91a7ca5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,5 @@ node_modules dist coverage .eslintrc.* -vite.config.ts \ No newline at end of file +vite.config.ts +packages/kit-headless/browsers/** \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4806d6a8..6c80f20d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,7 @@ on: jobs: test: runs-on: ubuntu-latest + name: Test NodeJS ${{ matrix.node_version }} strategy: matrix: diff --git a/.vscode/settings.json b/.vscode/settings.json index cf055a154..1a269d81d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,6 @@ ], "editor.codeActionsOnSave": { "source.removeUnusedImports": "explicit" - } + }, + "vitest.disableWorkspaceWarning": true } diff --git a/apps/website/src/components/animations/caveats.tsx b/apps/website/src/components/animations/caveats.tsx new file mode 100644 index 000000000..e58c54c42 --- /dev/null +++ b/apps/website/src/components/animations/caveats.tsx @@ -0,0 +1,49 @@ +import { component$ } from '@builder.io/qwik'; +import { Note, NoteStatus } from '../note/note'; // Adjust the import path based on your structure + +export const TopLayerAnimationsCaveats = component$(() => { + return ( + + Important Caveats for Animating Discrete Properties + + + + ); +}); diff --git a/apps/website/src/components/animations/compatability.tsx b/apps/website/src/components/animations/compatability.tsx new file mode 100644 index 000000000..ff15fa2a4 --- /dev/null +++ b/apps/website/src/components/animations/compatability.tsx @@ -0,0 +1,22 @@ +import { component$ } from '@builder.io/qwik'; +import { Note, NoteStatus } from '../note/note'; // Adjust the import path based on your structure + +export const BrowserAnimationsCompatability = component$(() => { + return ( + +
+

+ Browser Compatability +

+

+ + Browser versions that do not support the popover API natively + {' '} + have known issues when trying to use animations or transitions. If you need to + support legacy versions of browsers, please be sure to test this functionality + independently. +

+
+
+ ); +}); diff --git a/apps/website/src/components/mdx-components/index.tsx b/apps/website/src/components/mdx-components/index.tsx index 603655d39..0e983b5ab 100644 --- a/apps/website/src/components/mdx-components/index.tsx +++ b/apps/website/src/components/mdx-components/index.tsx @@ -11,6 +11,8 @@ import { KeyboardInteractionTable } from '../keyboard-interaction-table/keyboard import { Note } from '../note/note'; import { Showcase } from '../showcase/showcase'; import { StatusBanner } from '../status-banner/status-banner'; +import { TopLayerAnimationsCaveats } from '../animations/caveats'; +import { BrowserAnimationsCompatability } from '../animations/compatability'; export const components: Record = { p: component$>(({ ...props }) => { @@ -134,4 +136,6 @@ export const components: Record = { StatusBanner, Showcase, AutoAPI, + TopLayerAnimationsCaveats, + BrowserAnimationsCompatability, }; diff --git a/apps/website/src/routes/docs/headless/modal/examples/animatable.tsx b/apps/website/src/routes/docs/headless/modal/examples/animatable.tsx index cf48e055e..48866e4ce 100644 --- a/apps/website/src/routes/docs/headless/modal/examples/animatable.tsx +++ b/apps/website/src/routes/docs/headless/modal/examples/animatable.tsx @@ -1,9 +1,37 @@ import { component$, useStyles$ } from '@builder.io/qwik'; import { Modal, Label } from '@qwik-ui/headless'; -import styles from '../snippets/animation.css?inline'; export default component$(() => { - useStyles$(styles); + useStyles$(` + .modal-animation { + animation: modalClose 0.35s ease-in-out forwards; + } + + .modal-animation:popover-open { + animation: modalOpen 0.75s ease-in-out forwards; + } + + @keyframes modalOpen { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } + } + + @keyframes modalClose { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.9); + } + }`); return ( diff --git a/apps/website/src/routes/docs/headless/modal/examples/backdrop-animatable.tsx b/apps/website/src/routes/docs/headless/modal/examples/backdrop-animatable.tsx new file mode 100644 index 000000000..c20d32653 --- /dev/null +++ b/apps/website/src/routes/docs/headless/modal/examples/backdrop-animatable.tsx @@ -0,0 +1,42 @@ +import { component$, useStyles$ } from '@builder.io/qwik'; +import { Modal, Label } from '@qwik-ui/headless'; + +export default component$(() => { + useStyles$(` + .modal-animation[open]::backdrop { + animation: backdropFadeIn 0.75s ease-in-out forwards; + } + + @keyframes backdropFadeIn { + from { + background-color: rgba(0, 0, 0, 0); + } + to { + background-color: rgba(0, 0, 0, 0.65); + } + }`); + + return ( + + Open Modal + + Edit Profile + + You can update your profile here. Hit the save button when finished. + + + +
+ Cancel + Save Changes +
+
+
+ ); +}); diff --git a/apps/website/src/routes/docs/headless/modal/examples/transition.tsx b/apps/website/src/routes/docs/headless/modal/examples/transition.tsx index acd413a3d..1fe5248c6 100644 --- a/apps/website/src/routes/docs/headless/modal/examples/transition.tsx +++ b/apps/website/src/routes/docs/headless/modal/examples/transition.tsx @@ -1,9 +1,30 @@ import { component$, useStyles$ } from '@builder.io/qwik'; import { Modal, Label } from '@qwik-ui/headless'; -import styles from '../snippets/animation.css?inline'; export default component$(() => { - useStyles$(styles); + useStyles$(` + .modal-transition { + opacity: 0; + transform: scale(0.9); + transition: + opacity 0.35s ease-in-out, + transform 0.35s ease-in-out, + display 0.35s, + overlay 0.35s; + transition-behavior: allow-discrete; + } + + .modal-transition:popover-open { + opacity: 1; + transform: scale(1); + } + + @starting-style { + .modal-transition:popover-open { + opacity: 0; + transform: scale(0.9); + } + }`); return ( diff --git a/apps/website/src/routes/docs/headless/modal/index.mdx b/apps/website/src/routes/docs/headless/modal/index.mdx index bac835e26..02b460e8c 100644 --- a/apps/website/src/routes/docs/headless/modal/index.mdx +++ b/apps/website/src/routes/docs/headless/modal/index.mdx @@ -3,11 +3,9 @@ title: Qwik UI | Modal --- import topLayer from '../../../../../public/images/top-layer.webp'; - import { statusByComponent } from '~/_state/component-statuses'; -import {FeatureList} from '~/components/feature-list/feature-list'; # Modal @@ -170,33 +168,29 @@ This is done in a separate layer so that styles are easily overridable in consum ## Animations -Animating things to display none has historically been a significant challenge on the web. This is because display none is a `discrete` property, and is **unanimatable**. - -> There is currently efforts to solve this problem. [New CSS properties](https://developer.chrome.com/blog/entry-exit-animations/) have been introduced, but currently do not provide good enough browser support. +Modals require smooth entry and exit animations to enhance user experience. However, animating properties like display and overlay can be challenging because they are discrete properties and not traditionally animatable. -### Our current approach +Modern browsers have introduced discrete animation capabilities, allowing us to animate these properties effectively. Below, we'll explore how to implement animations and transitions for modals using keyframe animations and CSS transitions. -Qwik UI automatically detects any `animation` or `transition` declarations under the hood and waits for them to finish before closing the modal. If there is no animation, then it will close normally. +### Keyframe Animation Example -### Adding a transition +Keyframes are ideal for handling the entry and exit of the modal. Here's an example using modalOpen for opening and modalClose for closing the modal: - + -To add an transition, use the `data-open`, `data-closing` and `data-closed` data attributes. Above is a snippet where we transition both the modal and backdrop's opacity. +### Transition Declarations -### Adding an animation +Transitions are useful for animating properties like opacity and transform. Here's how to implement transitions for the modal: - + -To add an animation, it's the same as with transitions, using the `data-open` and `data-closing` data attributes. Below is a snippet of the animation example above. + ### Backdrop animations -Backdrop animations have also made significant progress in the last year, with support provided in over half of the major browsers, and close to 70% of users. - -To add a backdrop animation, make sure to use the `::backdrop` pseudo selector to the end of the `data-closing` or `data-open` classes. +To animate the modal's backdrop, use the `::backdrop` pseudo-element and include it in your keyframes or transitions: -> Firefox currently does not support backdrop animations. The fallback for browsers that do not support animated backdrops is the same as a non-animated backdrop. + ## Sheets diff --git a/apps/website/src/routes/docs/headless/popover/auto-api/api.ts b/apps/website/src/routes/docs/headless/popover/auto-api/api.ts new file mode 100644 index 000000000..4a65a83e8 --- /dev/null +++ b/apps/website/src/routes/docs/headless/popover/auto-api/api.ts @@ -0,0 +1,25 @@ +export const api = { + popover: [ + { + floating: [], + }, + { + 'popover-panel-arrow': [], + }, + { + 'popover-panel-impl': [], + }, + { + 'popover-panel': [], + }, + { + 'popover-root': [], + }, + { + 'popover-trigger': [], + }, + { + 'use-popover': [], + }, + ], +}; diff --git a/apps/website/src/routes/docs/headless/popover/examples/animation.tsx b/apps/website/src/routes/docs/headless/popover/examples/animation.tsx index c9ee5eb61..d435d5dfa 100644 --- a/apps/website/src/routes/docs/headless/popover/examples/animation.tsx +++ b/apps/website/src/routes/docs/headless/popover/examples/animation.tsx @@ -1,6 +1,40 @@ -import { component$ } from '@builder.io/qwik'; +import { component$, useStyles$ } from '@builder.io/qwik'; import { Popover } from '@qwik-ui/headless'; + export default component$(() => { + useStyles$(` + .popover-animation { + animation: popover-shrink 0.4s ease-in-out forwards; + } + + /* For exit animation */ + .popover-animation:popover-open { + animation: popover-grow 0.5s ease-in-out forwards; + } + + @keyframes popover-shrink { + from { + transform: scale(1); + display: block; + } + + to { + transform: scale(0); + display: none; + } + } + + @keyframes popover-grow { + from { + transform: scale(0); + } + + to { + transform: scale(1); + } + } + `); + return ( Popover Trigger diff --git a/apps/website/src/routes/docs/headless/popover/examples/transition.tsx b/apps/website/src/routes/docs/headless/popover/examples/transition.tsx index fe77898a5..9c7b5fdfb 100644 --- a/apps/website/src/routes/docs/headless/popover/examples/transition.tsx +++ b/apps/website/src/routes/docs/headless/popover/examples/transition.tsx @@ -1,6 +1,36 @@ -import { component$ } from '@builder.io/qwik'; +import { component$, useStyles$ } from '@builder.io/qwik'; import { Popover } from '@qwik-ui/headless'; + export default component$(() => { + useStyles$(` + .popover-transition { + opacity: 0; + transform: scale(0.5); + transition: + opacity 0.3s ease-out, + transform 0.3s ease-out, + display 0.3s, + overlay 0.3s; + transition-behavior: allow-discrete; + } + + .popover-transition:popover-open { + opacity: 1; + transform: scale(1); + transition: + opacity 0.3s ease-out, + transform 0.3s ease-out, + display 0.3s, + overlay 0.3s; + } + + @starting-style { + .popover-transition:popover-open { + opacity: 0; + transform: scale(0.5); + } + }`); + return ( Popover Trigger diff --git a/apps/website/src/routes/docs/headless/popover/index.mdx b/apps/website/src/routes/docs/headless/popover/index.mdx index 89f2e43eb..5b7fc9ab1 100644 --- a/apps/website/src/routes/docs/headless/popover/index.mdx +++ b/apps/website/src/routes/docs/headless/popover/index.mdx @@ -3,10 +3,10 @@ title: Qwik UI | Popover --- import { statusByComponent } from '~/_state/component-statuses'; +import Styles from './examples/styles'; -import Styles from './examples/styles'; # Popover @@ -138,41 +138,6 @@ It can help prevent overflow issues within your UI, and make sure your content i Use the `data-open` attribute on the `` component to specifically style the popup when it's open on all browsers. -### Cross-browser Animations - -Entry and exit animations have often been a frustrating experience in web development. Especially trying to animate between `display: none`, a discrete property. - -Until recently, discrete properties were not animatable. Luckily, there's [new properties to CSS](https://developer.chrome.com/blog/entry-exit-animations) that solve this problem. - -The browser support for these is similar to support of the popover API. That said, the Qwik UI team has done an awesome job of managing animations on polyfill browsers for you using the `data-open` and `data-closing` attributes. - -```css -.my-transition { - transition: - opacity 0.5s, - display 0.5s, - overlay 0.5s; - - /* on new-line so the declaration is valid in all browsers */ - transition-behavior: allow-discrete; - - /* starting style for all browsers */ - opacity: 0; -} - -.my-transition[data-open] { - opacity: 1; -} - -.my-transition[data-closing] { - opacity: 0; -} -``` - -Above is an example of a transition that works across browsers. The `allow-discrete` property allows us to transition both display and overlay. Overlay is a property that allows us to transition top-layer behavior. - -> In the rare case of a browser supporting the popover API, but not the overlay property, the element functionality is the same but does not transition. - ## Popover modes ### Auto @@ -332,80 +297,32 @@ We put it under an `@layer` so that it can be easily overridden when adding your ## Animations - - -To use an animation, add the following CSS classes to the component. - -- The `data-open` attribute determines the animation that happens when it is first opened. - -- The `data-closing` class determines what class is added when the listbox is **closed**. - -Here's the CSS imported from the example: - -```css -.popover-animation { - transform: scale(0); -} +Popovers present unique challenges for animations due to their reliance on the `display` property, which traditionally hasn't been animatable. Modern browsers solve this problem with discrete animations, which allow smooth transitions between `display: none` and `display: block`. This ensures that popovers remain visible for the entire duration of the animation. -.popover-animation[data-open] { - animation: popover-grow 0.5s ease-in-out forwards; -} +There are two main types of animations that can be applied to popovers in Qwik UI: -.popover-animation[data-closing] { - animation: popover-shrink 0.4s ease-in-out forwards; -} +- **Keyframe Animations**: For more complex entry and exit animations, such as growing or shrinking effects. +- **CSS Transitions**: For smoother, incremental property changes like opacity and scaling. -@keyframes popover-shrink { - from { - transform: scale(1); - } +To handle animations across all browsers, Qwik UI uses both keyframes and transitions, leveraging the native `:popover-open` pseudo-class and ensuring smooth state transitions with `transition-behavior: allow-discrete`. - to { - transform: scale(0); - } -} + -@keyframes popover-grow { - from { - transform: scale(0); - } +### Keyframe Animation Example - to { - transform: scale(1); - } -} -``` +Keyframes are ideal for handling the entry and exit of the popover. Below is an example using `popover-grow` for opening and `popover-shrink` for closing the popover: -> These classes also apply to transition declarations as well. Below is an example of that use case. + ### Transition declarations -Transitions use the same classes for entry and exit animations. Those being `data-open` and `data-closing`. They are explained more in the `Caveats` section. +CSS transitions are useful for animating properties like opacity and scale over time. Discrete properties like display and overlay can be handled using transition-behavior: allow-discrete. -> The [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) is another native solution that aims to solve animating between states. Support is currently in around **~71%** of browsers. + -CSS from the example: - -```css -.popover-transition { - opacity: 0; - transition: - opacity 0.5s, - display 0.5s, - overlay 0.5s; - transition-behavior: allow-discrete; -} - -.popover-transition[data-open] { - opacity: 1; -} - -.popover-transition[data-closing] { - opacity: 0; -} -``` +By using keyframe animations and CSS transitions together, we can achieve smooth entry and exit effects for popovers. Keyframes like `popover-grow` and `popover-shrink` handle scaling and visibility, while transitions focus on gradual changes to properties like `opacity`. Remember to include `display` and `overlay` in your transition properties and use `transition-behavior: allow-discrete` to ensure smooth animations across all browsers. ## Additional References @@ -463,21 +380,11 @@ To read more about the popover API you can check it out on: type: 'selector', description: 'Style the element when the popover is open.', }, - { - name: 'data-closing', - type: 'selector', - description: 'Style the element when the popover is closing.', - }, { name: 'data-closed', type: 'selector', description: 'Style the element when the popover is closed.', }, - { - name: 'data-closing', - type: 'class', - description: 'Class to animate exit behavior.', - }, ]} /> diff --git a/apps/website/src/routes/docs/headless/popover/snippets/popover.css b/apps/website/src/routes/docs/headless/popover/snippets/popover.css index 49d560371..02b7733de 100644 --- a/apps/website/src/routes/docs/headless/popover/snippets/popover.css +++ b/apps/website/src/routes/docs/headless/popover/snippets/popover.css @@ -30,35 +30,6 @@ 0 2px 4px -1px rgba(0, 0, 0, 0.06); } -.popover-transition { - opacity: 0; - transition: - opacity 0.5s, - display 0.5s, - overlay 0.5s; - transition-behavior: allow-discrete; -} - -.popover-transition[data-open] { - opacity: 1; -} - -.popover-transition[data-closing] { - opacity: 0; -} - -.popover-animation { - transform: scale(0); -} - -.popover-animation[data-open] { - animation: popover-grow 0.5s ease-in-out forwards; -} - -.popover-animation[data-closing] { - animation: popover-shrink 0.4s ease-in-out forwards; -} - .popover-invoker { border-radius: var(--border-radius); border: 2px dotted hsla(var(--border)); @@ -70,26 +41,6 @@ 0 2px 4px -1px rgba(0, 0, 0, 0.06); } -@keyframes popover-shrink { - from { - transform: scale(1); - } - - to { - transform: scale(0); - } -} - -@keyframes popover-grow { - from { - transform: scale(0); - } - - to { - transform: scale(1); - } -} - .popover-programmatic { max-width: 15.5rem; text-align: center; diff --git a/apps/website/src/routes/docs/headless/tooltip/examples/animation.tsx b/apps/website/src/routes/docs/headless/tooltip/examples/animation.tsx index 45321b077..e2c515b53 100644 --- a/apps/website/src/routes/docs/headless/tooltip/examples/animation.tsx +++ b/apps/website/src/routes/docs/headless/tooltip/examples/animation.tsx @@ -1,9 +1,36 @@ -import { component$ } from '@builder.io/qwik'; +import { component$, useStyles$ } from '@builder.io/qwik'; import { Tooltip } from '@qwik-ui/headless'; -import '../snippets/animation.css'; - export default component$(() => { + useStyles$(` + .tooltip-animation { + animation: tooltip-shrink 0.4s ease-in-out forwards; + } + + .tooltip-animation:popover-open { + animation: tooltip-grow 0.5s ease-in-out forwards; + } + + @keyframes tooltip-shrink { + from { + transform: scale(1); + display: block; + } + to { + transform: scale(0); + display: none; + } + } + + @keyframes tooltip-grow { + from { + transform: scale(0); + } + to { + transform: scale(1); + } + }`); + return ( Hover or Focus me diff --git a/apps/website/src/routes/docs/headless/tooltip/examples/styles.tsx b/apps/website/src/routes/docs/headless/tooltip/examples/styles.tsx new file mode 100644 index 000000000..2b567b637 --- /dev/null +++ b/apps/website/src/routes/docs/headless/tooltip/examples/styles.tsx @@ -0,0 +1,8 @@ +import { component$, useStyles$ } from '@builder.io/qwik'; +import styles from '../snippets/tooltip.css?inline'; + +export default component$(() => { + useStyles$(styles); + + return <>; +}); diff --git a/apps/website/src/routes/docs/headless/tooltip/examples/transition.tsx b/apps/website/src/routes/docs/headless/tooltip/examples/transition.tsx index 3f7bf3fcc..c932859e7 100644 --- a/apps/website/src/routes/docs/headless/tooltip/examples/transition.tsx +++ b/apps/website/src/routes/docs/headless/tooltip/examples/transition.tsx @@ -1,9 +1,27 @@ -import { component$ } from '@builder.io/qwik'; +import { component$, useStyles$ } from '@builder.io/qwik'; import { Tooltip } from '@qwik-ui/headless'; -import '../snippets/transition.css'; - export default component$(() => { + useStyles$(` + .tooltip-transition { + opacity: 0; + transform: scaleX(0); + transition: all 0.7s ease; + transition-behavior: allow-discrete; + } + + .tooltip-transition:popover-open { + opacity: 1; + transform: scaleX(1); + } + + @starting-style { + .tooltip-transition:popover-open { + opacity: 0; + transform: scaleX(0); + } + }`); + return ( Hover or Focus me diff --git a/apps/website/src/routes/docs/headless/tooltip/index.mdx b/apps/website/src/routes/docs/headless/tooltip/index.mdx index 31c2f77f5..55430f1fd 100644 --- a/apps/website/src/routes/docs/headless/tooltip/index.mdx +++ b/apps/website/src/routes/docs/headless/tooltip/index.mdx @@ -6,6 +6,8 @@ import { statusByComponent } from '~/_state/component-statuses'; +import Styles from './examples/styles'; + # Tooltip A text label that appears when a user hovers, focuses, or touches an element. @@ -115,10 +117,6 @@ Tooltips are useful for displaying contextual information or additional details Use the `data-open` and `data-closed` attributes on the `` component to specifically style the tooltip when it's open. -### Cross-browser Animations - -> Animations are not currently available in the Tooltip component. We are working on a solution to provide a more seamless experience. In the meantime, you can still use transitions. - ## Tooltip Behavior Tooltips show when hovering over or focusing on the trigger element and dismiss when moving the mouse away or losing focus. @@ -165,17 +163,31 @@ If Tailwind is the framework of choice, then styles can be added using the [arbi > The arbitrary variant can be customized/abstracted even further by [adding a variant](https://tailwindcss.com/docs/plugins#adding-variants) as a plugin in the tailwind config. +## Animations + +Tooltips rely on animations and transitions to provide smooth entry and exit effects. Like the Popover, tooltips benefit from modern CSS capabilities that allow animating between display: none and display: block. This ensures the tooltip remains visible for the entire duration of the animation. + + + +### Keyframe Animation Example + + + +Keyframes are used for complex entry and exit animations. In this case: + +`tooltip-grow` animates the tooltip to grow from scale 0 to scale 1 when it opens. + +`tooltip-shrink` animates the tooltip to shrink from scale 1 to scale 0 when it closes, eventually setting `display: none`. + ### Transition declarations -Transitions use the same classes for entry and exit animations. Those being `data-open` and `data-closed`. They are explained more in the `Caveats` section. +Transitions are ideal for animating properties like opacity and scale for smoother interactions: -> The [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API) is another native solution that aims to solve animating between states. Support is currently in around **~71%** of browsers. - -CSS from the example: +Here, transitions handle smooth opacity and scale changes. The `transition-behavior: allow-discrete` ensures that `display` and `overlay` animate properly. - + ## Events @@ -220,7 +232,7 @@ To read more about tooltips you can check it out on: description: 'The space between the trigger element and the tooltip.', }, { - name: 'data-closing"', + name: 'data-closing', type: 'selector', description: 'Style the element when the tooltip is closing. This occurs when the popover has a delay set.', }, diff --git a/apps/website/src/routes/docs/headless/tooltip/snippets/animation.css b/apps/website/src/routes/docs/headless/tooltip/snippets/animation.css deleted file mode 100644 index 0ef140ab2..000000000 --- a/apps/website/src/routes/docs/headless/tooltip/snippets/animation.css +++ /dev/null @@ -1,29 +0,0 @@ -.tooltip-animation { - transform: scale(0); -} - -.tooltip-animation[data-open] { - animation: tooltip-grow 0.5s ease-in-out forwards; -} - -.tooltip-animation[data-closing] { - animation: tooltip-shrink 0.4s ease-in-out forwards; -} - -@keyframes tooltip-shrink { - from { - transform: scale(1); - } - to { - transform: scale(0); - } -} - -@keyframes tooltip-grow { - from { - transform: scale(0); - } - to { - transform: scale(1); - } -} diff --git a/apps/website/src/routes/docs/headless/tooltip/snippets/transition.css b/apps/website/src/routes/docs/headless/tooltip/snippets/transition.css deleted file mode 100644 index 7c7266a35..000000000 --- a/apps/website/src/routes/docs/headless/tooltip/snippets/transition.css +++ /dev/null @@ -1,16 +0,0 @@ -.tooltip-transition { - opacity: 0; - transition: - opacity 0.5s, - display 0.5s, - overlay 0.5s; - transition-behavior: allow-discrete; -} - -.tooltip-transition[data-open] { - opacity: 1; -} - -.tooltip-transition[data-closed] { - opacity: 0; -} diff --git a/package.json b/package.json index a3356db30..c9d994bdb 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "test.utils": "nx test utils" }, "simple-git-hooks": { - "pre-commit": "pnpm format.staged" + "pre-commit": "pnpm format.staged && pnpm lint" }, "packageManager": "pnpm@9.7.0", "devDependencies": { diff --git a/packages/kit-headless/src/components/popover/popover-panel-impl.tsx b/packages/kit-headless/src/components/popover/popover-panel-impl.tsx index d3fbe02e7..d4a379ee0 100644 --- a/packages/kit-headless/src/components/popover/popover-panel-impl.tsx +++ b/packages/kit-headless/src/components/popover/popover-panel-impl.tsx @@ -14,7 +14,6 @@ import { import { isServer } from '@builder.io/qwik/build'; import popoverStyles from './popover.css?inline'; import { popoverContextId } from './popover-context'; -import { supportShowAnimation, supportClosingAnimation } from './utils'; import { useCombinedRef } from '../../hooks/combined-refs'; // We don't need a provider, that way we connect all context to the root @@ -116,11 +115,15 @@ export const HPopoverPanelImpl = component$((props: PropsOf<'div'>) => { $(async (e) => { if (!context.panelRef?.value) return; - if (e.newState === 'open' && context.panelRef.value) { - await supportShowAnimation(context.panelRef.value, isPolyfillSig.value); + const popover = context.panelRef.value; + + if (e.newState === 'open') { + delete popover.dataset.closed; + popover.dataset.open = ''; } if (e.newState === 'closed') { - supportClosingAnimation(context.panelRef.value); + delete popover.dataset.open; + popover.dataset.closed = ''; } }), props.onBeforeToggle$, diff --git a/packages/kit-headless/src/components/popover/utils.ts b/packages/kit-headless/src/components/popover/utils.ts deleted file mode 100644 index c49d86104..000000000 --- a/packages/kit-headless/src/components/popover/utils.ts +++ /dev/null @@ -1,59 +0,0 @@ -// TODO: fix the setTimeouts -// TODO: move this file into the usePopover hook - -/** - * Adds CSS-Class to support popover-opening-animation - */ -export async function supportShowAnimation(popover: HTMLElement, isPolyfill: boolean) { - const { transitionDuration } = getComputedStyle(popover); - - async function delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - - const delayTime = isPolyfill ? 10 : 5; - - if (transitionDuration !== '0s') { - await delay(delayTime); - } - - popover.classList.add('popover-showing'); - popover.classList.remove('popover-closing'); - popover.dataset.open = ''; - popover.removeAttribute('data-closing'); - popover.removeAttribute('data-closed'); -} - -/** - * Listens for animation/transition events in order to - * remove Animation-CSS-Classes after animation/transition ended. - */ -export function supportClosingAnimation(popover: HTMLElement) { - popover.classList.remove('popover-showing'); - popover.classList.add('popover-closing'); - popover.dataset.closing = ''; - - const { animationDuration, transitionDuration } = getComputedStyle(popover); - - const runAnimationEnd = () => { - popover.classList.remove('popover-closing'); - popover.removeAttribute('data-closing'); - popover.dataset.closed = ''; - }; - - const runTransitionEnd = () => { - popover.classList.remove('popover-closing'); - popover.removeAttribute('data-closing'); - popover.dataset.closed = ''; - }; - - if (animationDuration !== '0s') { - popover.addEventListener('animationend', runAnimationEnd, { once: true }); - } else if (transitionDuration !== '0s') { - popover.addEventListener('transitionend', runTransitionEnd, { once: true }); - } else { - popover.classList.remove('popover-closing'); - popover.removeAttribute('data-closing'); - popover.dataset.closed = ''; - } -}