reshaped 4.2.0 → 4.3.0-canary.0
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/dist/bundle.css +1 -1
- package/dist/bundle.js +2 -2
- package/dist/components/Autocomplete/Autocomplete.js +1 -1
- package/dist/components/ContextMenu/tests/ContextMenu.stories.d.ts +7 -0
- package/dist/components/ContextMenu/tests/ContextMenu.stories.js +99 -1
- package/dist/components/DropdownMenu/DropdownMenu.js +1 -1
- package/dist/components/Flyout/Flyout.module.css +1 -1
- package/dist/components/Flyout/Flyout.types.d.ts +1 -0
- package/dist/components/Flyout/FlyoutContent.js +3 -2
- package/dist/components/Flyout/FlyoutControlled.js +23 -4
- package/dist/components/Flyout/tests/Flyout.stories.d.ts +7 -0
- package/dist/components/Flyout/tests/Flyout.stories.js +142 -1
- package/dist/components/Modal/Modal.js +13 -171
- package/dist/components/Modal/Modal.module.css +1 -1
- package/dist/components/Modal/Modal.types.d.ts +2 -1
- package/dist/components/Modal/useModalDrag.d.ts +21 -0
- package/dist/components/Modal/useModalDrag.js +256 -0
- package/dist/components/Overlay/Overlay.js +5 -4
- package/dist/components/Overlay/Overlay.module.css +1 -1
- package/dist/components/ScrollArea/ScrollArea.js +29 -16
- package/dist/components/ScrollArea/ScrollArea.module.css +1 -1
- package/dist/components/ScrollArea/ScrollArea.types.d.ts +1 -1
- package/dist/components/Select/SelectCustomControlled.js +1 -1
- package/dist/components/Select/tests/Select.stories.js +12 -1
- package/dist/components/_private/Freeze/Freeze.d.ts +4 -0
- package/dist/components/_private/Freeze/Freeze.js +46 -0
- package/dist/components/_private/Freeze/Freeze.module.css +1 -0
- package/dist/components/_private/Freeze/Freeze.types.d.ts +14 -0
- package/dist/components/_private/Freeze/Freeze.types.js +1 -0
- package/dist/components/_private/Freeze/index.d.ts +1 -0
- package/dist/components/_private/Freeze/index.js +1 -0
- package/dist/components/_private/Freeze/tests/Freeze.stories.d.ts +7 -0
- package/dist/components/_private/Freeze/tests/Freeze.stories.js +88 -0
- package/dist/hooks/_internal/useSingletonHotkeys.js +4 -0
- package/dist/hooks/tests/useHotkeys.stories.d.ts +3 -0
- package/dist/hooks/tests/useHotkeys.stories.js +18 -1
- package/dist/hooks/tests/useOnClickOutside.stories.d.ts +6 -0
- package/dist/hooks/tests/useOnClickOutside.stories.js +39 -1
- package/dist/hooks/useOnClickOutside.js +12 -12
- package/dist/utilities/animation.d.ts +17 -0
- package/dist/utilities/animation.js +73 -0
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
.root{--rs-modal-container-width:100vw;--rs-modal-container-height:100vh;transition:var(--rs-duration-fast) var(--rs-easing-accelerate);transition-property:transform,opacity;background:var(--rs-color-background-elevation-overlay);color:var(--rs-color-foreground-neutral);box-shadow:var(--rs-shadow-overlay);will-change:transform,opacity;outline:none;overscroll-behavior:none}[data-rs-keyboard] .root:focus{outline:var(--rs-outline)}.root.--contained{--rs-modal-container-width:100%;--rs-modal-container-height:100%}.root{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);--rs-modal-size-m:var(--rs-modal-size-s);--rs-modal-size-l:var(--rs-modal-size-m);--rs-modal-size-xl:var(--rs-modal-size-l);--rs-modal-size:var(--rs-modal-size-s)}.--position-center{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center.--active,[dir=rtl] .--position-center.--active{transform:translate(0);opacity:1}.--position-bottom{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom.--active,[dir=rtl] .--position-bottom.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start{transform:translate(100%)}.--position-start.--active,[dir=rtl] .--position-start.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end{transform:translate(-100%)}.--position-end.--active,[dir=rtl] .--position-end.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen.--active,[dir=rtl] .--position-full-screen.--active{transform:translate(0);opacity:1}.--active,[dir=rtl] .--active{transition-timing-function:var(--rs-easing-decelerate)}.--dragging{transition:none}.--overflow-visible{overflow:visible}@media (--rs-viewport-m ){.root{--rs-modal-size:var(--rs-modal-size-m)}.--position-center--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--m.--active,[dir=rtl] .--position-center--m.--active{transform:translate(0);opacity:1}.--position-bottom--m{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--m.--active,[dir=rtl] .--position-bottom--m.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--m{transform:translate(100%)}.--position-start--m.--active,[dir=rtl] .--position-start--m.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--m{transform:translate(-100%)}.--position-end--m.--active,[dir=rtl] .--position-end--m.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--m{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--m.--active,[dir=rtl] .--position-full-screen--m.--active{transform:translate(0);opacity:1}}@media (--rs-viewport-l ){.root{--rs-modal-size:var(--rs-modal-size-l)}.--position-center--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--l.--active,[dir=rtl] .--position-center--l.--active{transform:translate(0);opacity:1}.--position-bottom--l{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--l.--active,[dir=rtl] .--position-bottom--l.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--l{transform:translate(100%)}.--position-start--l.--active,[dir=rtl] .--position-start--l.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--l{transform:translate(-100%)}.--position-end--l.--active,[dir=rtl] .--position-end--l.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--l{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--l.--active,[dir=rtl] .--position-full-screen--l.--active{transform:translate(0);opacity:1}}@media (--rs-viewport-xl ){.root{--rs-modal-size:var(--rs-modal-size-xl)}.--position-center--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--xl.--active,[dir=rtl] .--position-center--xl.--active{transform:translate(0);opacity:1}.--position-bottom--xl{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--xl.--active,[dir=rtl] .--position-bottom--xl.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--xl{transform:translate(100%)}.--position-start--xl.--active,[dir=rtl] .--position-start--xl.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--xl{transform:translate(-100%)}.--position-end--xl.--active,[dir=rtl] .--position-end--xl.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--xl{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--xl.--active,[dir=rtl] .--position-full-screen--xl.--active{transform:translate(0);opacity:1}}
|
|
1
|
+
@property --rs-modal-drag{syntax:"<length>";initial-value:0;inherits:false}.root{--rs-modal-container-width:100vw;--rs-modal-container-height:100vh;transition:var(--rs-duration-fast) var(--rs-easing-accelerate);transition-property:transform,opacity;background:var(--rs-color-background-elevation-overlay);color:var(--rs-color-foreground-neutral);box-shadow:var(--rs-shadow-overlay);will-change:transform,opacity;outline:none;overscroll-behavior:none}[data-rs-keyboard] .root:focus{outline:var(--rs-outline)}.root.--contained{--rs-modal-container-width:100%;--rs-modal-container-height:100%}.root{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);--rs-modal-size-m:var(--rs-modal-size-s);--rs-modal-size-l:var(--rs-modal-size-m);--rs-modal-size-xl:var(--rs-modal-size-l);--rs-modal-size:var(--rs-modal-size-s)}.--position-center{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center.--active,[dir=rtl] .--position-center.--active{transform:translate(0);opacity:1}.--position-bottom{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom.--active,[dir=rtl] .--position-bottom.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start{transform:translate(100%)}.--position-start.--active,[dir=rtl] .--position-start.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end{transform:translate(-100%)}.--position-end.--active,[dir=rtl] .--position-end.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen.--active,[dir=rtl] .--position-full-screen.--active{transform:translate(0);opacity:1}.--active,[dir=rtl] .--active{transition-timing-function:var(--rs-easing-decelerate)}.--dragging{transition:none}.--overflow-visible{overflow:visible}@media (--rs-viewport-m ){.root{--rs-modal-size:var(--rs-modal-size-m)}.--position-center--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--m.--active,[dir=rtl] .--position-center--m.--active{transform:translate(0);opacity:1}.--position-bottom--m{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--m.--active,[dir=rtl] .--position-bottom--m.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--m{transform:translate(100%)}.--position-start--m.--active,[dir=rtl] .--position-start--m.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--m{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--m{transform:translate(-100%)}.--position-end--m.--active,[dir=rtl] .--position-end--m.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--m{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--m.--active,[dir=rtl] .--position-full-screen--m.--active{transform:translate(0);opacity:1}}@media (--rs-viewport-l ){.root{--rs-modal-size:var(--rs-modal-size-l)}.--position-center--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--l.--active,[dir=rtl] .--position-center--l.--active{transform:translate(0);opacity:1}.--position-bottom--l{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--l.--active,[dir=rtl] .--position-bottom--l.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--l{transform:translate(100%)}.--position-start--l.--active,[dir=rtl] .--position-start--l.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--l{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--l{transform:translate(-100%)}.--position-end--l.--active,[dir=rtl] .--position-end--l.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--l{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--l.--active,[dir=rtl] .--position-full-screen--l.--active{transform:translate(0);opacity:1}}@media (--rs-viewport-xl ){.root{--rs-modal-size:var(--rs-modal-size-xl)}.--position-center--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:auto;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x8));max-height:none;margin:var(--rs-unit-x4);inset:0;position:relative;border-radius:var(--rs-radius-large);overflow:hidden;overscroll-behavior:auto;opacity:0;transform:translateY(var(--rs-unit-x4))}.--position-center--xl.--active,[dir=rtl] .--position-center--xl.--active{transform:translate(0);opacity:1}.--position-bottom--xl{--rs-modal-size-s:auto;width:100%;height:var(--rs-modal-size);max-width:100%;max-height:calc(var(--rs-modal-container-height) - var(--rs-unit-x4));margin:0;margin-top:var(--rs-unit-x4);inset:0;inset-block-start:auto;transform:translateY(100%);position:absolute;border-radius:var(--rs-radius-large) var(--rs-radius-large) 0 0;overflow:auto;overscroll-behavior:none;opacity:1}.--position-bottom--xl.--active,[dir=rtl] .--position-bottom--xl.--active{transform:translateY(max(var(--rs-modal-drag,0px),0px))}.--position-start--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-end:var(--rs-unit-x4);inset:0;inset-inline-end:auto;transform:translate(-100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-start--xl{transform:translate(100%)}.--position-start--xl.--active,[dir=rtl] .--position-start--xl.--active{transform:translate(min(var(--rs-modal-drag,0px),0px))}.--position-end--xl{--rs-modal-size-s:calc(var(--rs-unit-x1) * 100);width:var(--rs-modal-size);height:100%;max-width:calc(var(--rs-modal-container-width) - var(--rs-unit-x4));max-height:100%;margin:0;margin-inline-start:var(--rs-unit-x4);inset:0;inset-inline-start:auto;transform:translate(100%);position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:1}[dir=rtl] .--position-end--xl{transform:translate(-100%)}.--position-end--xl.--active,[dir=rtl] .--position-end--xl.--active{transform:translate(max(var(--rs-modal-drag,0px),0px))}.--position-full-screen--xl{--rs-modal-size-s:100%;width:100%;height:100%;max-width:var(--rs-modal-container-width);max-height:100%;margin:0;inset:0;inset-block-start:auto;transform:translateY(var(--rs-unit-x4));position:absolute;border-radius:0;overflow:auto;overscroll-behavior:none;opacity:0}.--position-full-screen--xl.--active,[dir=rtl] .--position-full-screen--xl.--active{transform:translate(0);opacity:1}}
|
|
@@ -3,6 +3,7 @@ import type { ClassName } from "@reshaped/utilities";
|
|
|
3
3
|
import type { OverlayCloseReason, OverlayProps } from "../../components/Overlay";
|
|
4
4
|
import type * as G from "../../types/global";
|
|
5
5
|
import type { Attributes } from "../../types/global";
|
|
6
|
+
export type Position = "center" | "end" | "bottom" | "start" | "full-screen";
|
|
6
7
|
export type Context = {
|
|
7
8
|
id: string;
|
|
8
9
|
titleMounted: boolean;
|
|
@@ -22,7 +23,7 @@ export type Props = {
|
|
|
22
23
|
/** Node for inserting children */
|
|
23
24
|
children?: React.ReactNode;
|
|
24
25
|
/** Component position on the screen */
|
|
25
|
-
position?: G.Responsive<
|
|
26
|
+
position?: G.Responsive<Position>;
|
|
26
27
|
/** Component size, literal css value */
|
|
27
28
|
size?: G.Responsive<string>;
|
|
28
29
|
/** Component padding, unit token multiplier */
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { OverlayInstance } from "../../components/Overlay";
|
|
2
|
+
import type * as T from "./Modal.types";
|
|
3
|
+
/**
|
|
4
|
+
* Swipe gesture closing the modal, moving it with the finger and continuing
|
|
5
|
+
* the release animation from the velocity the gesture ended with
|
|
6
|
+
*/
|
|
7
|
+
declare const useModalDrag: (args: {
|
|
8
|
+
position: T.Position;
|
|
9
|
+
rootRef: React.RefObject<HTMLDivElement | null>;
|
|
10
|
+
overlayRef: React.RefObject<OverlayInstance | null>;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
transparentOverlay?: boolean;
|
|
14
|
+
containerRef?: T.Props["containerRef"];
|
|
15
|
+
onClose?: T.Props["onClose"];
|
|
16
|
+
}) => {
|
|
17
|
+
dragging: boolean;
|
|
18
|
+
handleTouchStart: (e: React.TouchEvent) => void;
|
|
19
|
+
handleTransitionEnd: (e: React.TransitionEvent) => void;
|
|
20
|
+
};
|
|
21
|
+
export default useModalDrag;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { enableScroll, disableScroll } from "@reshaped/utilities/internal";
|
|
4
|
+
import useHandlerRef from "../../hooks/useHandlerRef";
|
|
5
|
+
import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect";
|
|
6
|
+
import { resolveSpringTransition } from "../../utilities/animation";
|
|
7
|
+
/** Distance in px the modal has to be dragged before it starts following the finger */
|
|
8
|
+
const THRESHOLD = 32;
|
|
9
|
+
/** Distance in px on the axis perpendicular to the drag axis cancelling the gesture */
|
|
10
|
+
const OPPOSITE_THRESHOLD = 100;
|
|
11
|
+
/** Screen edge zone in px reserved for the browser back gesture */
|
|
12
|
+
const EDGE_BOUNDARY = 32;
|
|
13
|
+
/** Distance in px after which the modal content stops scrolling for the rest of the gesture */
|
|
14
|
+
const SCROLL_LOCK_THRESHOLD = 8;
|
|
15
|
+
/** Velocity in px/ms closing the modal on release regardless of the dragged distance */
|
|
16
|
+
const VELOCITY_THRESHOLD = 0.4;
|
|
17
|
+
/** Time window in ms used for measuring the release velocity */
|
|
18
|
+
const VELOCITY_WINDOW = 100;
|
|
19
|
+
const VELOCITY_SAMPLES = 10;
|
|
20
|
+
const getInitialState = () => ({
|
|
21
|
+
origin: 0,
|
|
22
|
+
oppositeOrigin: 0,
|
|
23
|
+
previous: 0,
|
|
24
|
+
seed: 0,
|
|
25
|
+
distance: 0,
|
|
26
|
+
offset: 0,
|
|
27
|
+
size: 1,
|
|
28
|
+
samples: [],
|
|
29
|
+
closing: false,
|
|
30
|
+
returning: false,
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* Distance in px the modal is currently rendered at from its open position, towards the closing direction.
|
|
34
|
+
* Resolved from the transform so a gesture can pick up an animation that is still in flight,
|
|
35
|
+
* the dragging class disables transitions and would otherwise snap the modal to the animation target
|
|
36
|
+
*/
|
|
37
|
+
const getRenderedOffset = (el, args) => {
|
|
38
|
+
const { transform } = window.getComputedStyle(el);
|
|
39
|
+
if (!transform || transform === "none")
|
|
40
|
+
return 0;
|
|
41
|
+
const matrix = new DOMMatrixReadOnly(transform);
|
|
42
|
+
return Math.max(0, (args.axis === "y" ? matrix.m42 : matrix.m41) * args.closingSign);
|
|
43
|
+
};
|
|
44
|
+
/** Velocity in px/ms measured over the most recent samples, signed on the drag axis */
|
|
45
|
+
const getVelocity = (state, time) => {
|
|
46
|
+
const samples = state.samples.filter((sample) => time - sample.time <= VELOCITY_WINDOW);
|
|
47
|
+
const first = samples[0];
|
|
48
|
+
const last = samples[samples.length - 1];
|
|
49
|
+
if (!first || !last || first === last)
|
|
50
|
+
return 0;
|
|
51
|
+
return (last.coordinate - first.coordinate) / (last.time - first.time);
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Swipe gesture closing the modal, moving it with the finger and continuing
|
|
55
|
+
* the release animation from the velocity the gesture ended with
|
|
56
|
+
*/
|
|
57
|
+
const useModalDrag = (args) => {
|
|
58
|
+
const { position, rootRef, overlayRef, active, disabled, transparentOverlay, containerRef } = args;
|
|
59
|
+
const onCloseRef = useHandlerRef(args.onClose);
|
|
60
|
+
const [dragging, setDragging] = useState(false);
|
|
61
|
+
const stateRef = useRef(getInitialState());
|
|
62
|
+
const frameRef = useRef(null);
|
|
63
|
+
const axis = position === "bottom" ? "y" : "x";
|
|
64
|
+
// Start drawers close towards the negative coordinates
|
|
65
|
+
const closingSign = position === "start" ? -1 : 1;
|
|
66
|
+
const render = useCallback(() => {
|
|
67
|
+
const rootEl = rootRef.current;
|
|
68
|
+
const state = stateRef.current;
|
|
69
|
+
if (!rootEl)
|
|
70
|
+
return;
|
|
71
|
+
// A gesture picking up an animation starts on an already moving modal, so it skips the dead zone
|
|
72
|
+
const threshold = state.seed ? 0 : THRESHOLD;
|
|
73
|
+
const closingDistance = state.seed + state.distance;
|
|
74
|
+
state.offset = Math.max(0, closingDistance - threshold);
|
|
75
|
+
rootEl.style.setProperty("--rs-modal-drag", `${state.offset * closingSign}px`);
|
|
76
|
+
if (transparentOverlay)
|
|
77
|
+
return;
|
|
78
|
+
const progress = Math.min(1, closingDistance / state.size);
|
|
79
|
+
overlayRef.current?.setOpacity(1 - progress * 0.5);
|
|
80
|
+
}, [closingSign, transparentOverlay, rootRef, overlayRef]);
|
|
81
|
+
const reset = useCallback(() => {
|
|
82
|
+
stateRef.current = getInitialState();
|
|
83
|
+
render();
|
|
84
|
+
}, [render]);
|
|
85
|
+
const setScrollLock = useCallback((locked) => {
|
|
86
|
+
const style = rootRef.current?.style;
|
|
87
|
+
if (locked)
|
|
88
|
+
style?.setProperty("overflow", "hidden");
|
|
89
|
+
else
|
|
90
|
+
style?.removeProperty("overflow");
|
|
91
|
+
}, [rootRef]);
|
|
92
|
+
/**
|
|
93
|
+
* Continues the release animation from the current gesture velocity,
|
|
94
|
+
* velocity is passed in px/ms and is positive when moving towards the closing direction
|
|
95
|
+
*/
|
|
96
|
+
const setReleaseTransition = useCallback((releaseArgs) => {
|
|
97
|
+
const rootEl = rootRef.current;
|
|
98
|
+
const { offset, size } = stateRef.current;
|
|
99
|
+
const displacement = offset - (releaseArgs.closing ? size : 0);
|
|
100
|
+
// Without a displacement no transition will run and the override would stay applied
|
|
101
|
+
if (!rootEl || !displacement)
|
|
102
|
+
return;
|
|
103
|
+
const { duration, easing } = resolveSpringTransition({
|
|
104
|
+
displacement,
|
|
105
|
+
velocity: releaseArgs.velocity,
|
|
106
|
+
});
|
|
107
|
+
rootEl.style.setProperty("transition-duration", `${Math.round(duration)}ms`);
|
|
108
|
+
rootEl.style.setProperty("transition-timing-function", easing);
|
|
109
|
+
}, [rootRef]);
|
|
110
|
+
const resetReleaseTransition = useCallback(() => {
|
|
111
|
+
const style = rootRef.current?.style;
|
|
112
|
+
style?.removeProperty("transition-duration");
|
|
113
|
+
style?.removeProperty("transition-timing-function");
|
|
114
|
+
}, [rootRef]);
|
|
115
|
+
const handleTouchStart = (e) => {
|
|
116
|
+
if (disabled || position === "center")
|
|
117
|
+
return;
|
|
118
|
+
// Prevent swipe to close from happening when user is working with text selection
|
|
119
|
+
if (window.getSelection()?.toString())
|
|
120
|
+
return;
|
|
121
|
+
const rootEl = rootRef.current;
|
|
122
|
+
const touch = e.targetTouches[0];
|
|
123
|
+
let currentEl = e.target;
|
|
124
|
+
while (currentEl && (currentEl === rootEl || rootEl?.contains(currentEl))) {
|
|
125
|
+
// Prioritize scrolling over modal swiping
|
|
126
|
+
if (currentEl.scrollTop !== 0 || currentEl.scrollLeft !== 0)
|
|
127
|
+
return;
|
|
128
|
+
// Start dragging only when starting on static elements
|
|
129
|
+
if (currentEl.matches("input,textarea"))
|
|
130
|
+
return;
|
|
131
|
+
currentEl = currentEl.parentElement;
|
|
132
|
+
}
|
|
133
|
+
// Prevent the drag handling when browser is trying to navigate to a previous page
|
|
134
|
+
if (position === "start" && touch.clientX < EDGE_BOUNDARY)
|
|
135
|
+
return;
|
|
136
|
+
// Measured before the state is replaced, a transition in flight resolves to its current position
|
|
137
|
+
const seed = active && rootEl ? getRenderedOffset(rootEl, { axis, closingSign }) : 0;
|
|
138
|
+
const origin = axis === "y" ? touch.clientY : touch.clientX;
|
|
139
|
+
// The previous gesture can be left unresolved, e.g. when the modal stays open after a drag close
|
|
140
|
+
stateRef.current = {
|
|
141
|
+
...getInitialState(),
|
|
142
|
+
origin,
|
|
143
|
+
oppositeOrigin: axis === "y" ? touch.clientX : touch.clientY,
|
|
144
|
+
previous: origin,
|
|
145
|
+
seed,
|
|
146
|
+
size: rootEl ? Math.max(1, axis === "y" ? rootEl.clientHeight : rootEl.clientWidth) : 1,
|
|
147
|
+
};
|
|
148
|
+
resetReleaseTransition();
|
|
149
|
+
render();
|
|
150
|
+
if (containerRef?.current)
|
|
151
|
+
disableScroll();
|
|
152
|
+
setDragging(true);
|
|
153
|
+
};
|
|
154
|
+
const handleTransitionEnd = (e) => {
|
|
155
|
+
if (e.propertyName !== "transform")
|
|
156
|
+
return;
|
|
157
|
+
if (e.currentTarget !== e.target)
|
|
158
|
+
return;
|
|
159
|
+
resetReleaseTransition();
|
|
160
|
+
// Once modal is closed - reset its drag data
|
|
161
|
+
if (active)
|
|
162
|
+
return;
|
|
163
|
+
reset();
|
|
164
|
+
setScrollLock(false);
|
|
165
|
+
};
|
|
166
|
+
// Modal returns to its open position after the dragging class is removed,
|
|
167
|
+
// so the transition is applied to the reset instead of snapping back
|
|
168
|
+
useIsomorphicLayoutEffect(() => {
|
|
169
|
+
if (dragging || !stateRef.current.returning)
|
|
170
|
+
return;
|
|
171
|
+
reset();
|
|
172
|
+
}, [dragging, reset]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!dragging)
|
|
175
|
+
return;
|
|
176
|
+
const handleTouchMove = (e) => {
|
|
177
|
+
const state = stateRef.current;
|
|
178
|
+
const rootEl = rootRef.current;
|
|
179
|
+
if (rootEl?.scrollTop !== 0 || rootEl?.scrollLeft !== 0)
|
|
180
|
+
return;
|
|
181
|
+
const touch = e.targetTouches[0];
|
|
182
|
+
const coordinate = axis === "y" ? touch.clientY : touch.clientX;
|
|
183
|
+
const oppositeCoordinate = axis === "y" ? touch.clientX : touch.clientY;
|
|
184
|
+
const travel = Math.abs(coordinate - state.origin);
|
|
185
|
+
const oppositeTravel = Math.abs(oppositeCoordinate - state.oppositeOrigin);
|
|
186
|
+
// Positive when moving towards the closing direction
|
|
187
|
+
const delta = (coordinate - state.previous) * closingSign;
|
|
188
|
+
state.previous = coordinate;
|
|
189
|
+
// For start/end drawers - ignore the swiping
|
|
190
|
+
// If user is scrolling vertically more than swiping
|
|
191
|
+
if (axis === "x" && (travel < oppositeTravel || oppositeTravel > OPPOSITE_THRESHOLD)) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (travel > SCROLL_LOCK_THRESHOLD && delta > 0)
|
|
195
|
+
setScrollLock(true);
|
|
196
|
+
// Clamped so the modal can't be dragged past its open position
|
|
197
|
+
state.distance = Math.max(-state.seed, state.distance + delta);
|
|
198
|
+
state.samples.push({ time: e.timeStamp, coordinate });
|
|
199
|
+
if (state.samples.length > VELOCITY_SAMPLES)
|
|
200
|
+
state.samples.shift();
|
|
201
|
+
if (frameRef.current !== null)
|
|
202
|
+
return;
|
|
203
|
+
frameRef.current = window.requestAnimationFrame(() => {
|
|
204
|
+
frameRef.current = null;
|
|
205
|
+
render();
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
const handleTouchEnd = (e) => {
|
|
209
|
+
const state = stateRef.current;
|
|
210
|
+
// Positive when moving towards the closing direction
|
|
211
|
+
const velocity = getVelocity(state, e.timeStamp) * closingSign;
|
|
212
|
+
const flicked = velocity > VELOCITY_THRESHOLD;
|
|
213
|
+
const dragged = state.distance > THRESHOLD && velocity >= 0;
|
|
214
|
+
state.closing = flicked || dragged;
|
|
215
|
+
state.returning = !state.closing;
|
|
216
|
+
// Apply the pending frame before the release so the transition starts from the current position
|
|
217
|
+
if (frameRef.current !== null) {
|
|
218
|
+
window.cancelAnimationFrame(frameRef.current);
|
|
219
|
+
frameRef.current = null;
|
|
220
|
+
render();
|
|
221
|
+
}
|
|
222
|
+
setReleaseTransition({ closing: state.closing, velocity });
|
|
223
|
+
if (containerRef?.current)
|
|
224
|
+
enableScroll();
|
|
225
|
+
if (state.returning)
|
|
226
|
+
setScrollLock(false);
|
|
227
|
+
setDragging(false);
|
|
228
|
+
if (state.closing)
|
|
229
|
+
onCloseRef.current?.({ reason: "drag" });
|
|
230
|
+
};
|
|
231
|
+
document.addEventListener("touchmove", handleTouchMove, { passive: true });
|
|
232
|
+
document.addEventListener("touchend", handleTouchEnd, { passive: true });
|
|
233
|
+
return () => {
|
|
234
|
+
if (frameRef.current !== null) {
|
|
235
|
+
window.cancelAnimationFrame(frameRef.current);
|
|
236
|
+
frameRef.current = null;
|
|
237
|
+
}
|
|
238
|
+
if (!stateRef.current.closing)
|
|
239
|
+
setScrollLock(false);
|
|
240
|
+
document.removeEventListener("touchmove", handleTouchMove);
|
|
241
|
+
document.removeEventListener("touchend", handleTouchEnd);
|
|
242
|
+
};
|
|
243
|
+
}, [
|
|
244
|
+
dragging,
|
|
245
|
+
axis,
|
|
246
|
+
closingSign,
|
|
247
|
+
containerRef,
|
|
248
|
+
rootRef,
|
|
249
|
+
onCloseRef,
|
|
250
|
+
render,
|
|
251
|
+
setScrollLock,
|
|
252
|
+
setReleaseTransition,
|
|
253
|
+
]);
|
|
254
|
+
return { dragging, handleTouchStart, handleTransitionEnd };
|
|
255
|
+
};
|
|
256
|
+
export default useModalDrag;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { TrapFocus, classNames } from "@reshaped/utilities";
|
|
5
5
|
import Portal from "../../components/_private/Portal";
|
|
@@ -21,6 +21,7 @@ const Overlay = (props) => {
|
|
|
21
21
|
const [animated, setAnimated] = React.useState(false);
|
|
22
22
|
const [offset, setOffset] = React.useState([0, 0]);
|
|
23
23
|
const rootRef = React.useRef(null);
|
|
24
|
+
const backdropRef = React.useRef(null);
|
|
24
25
|
const scopeRef = React.useRef(null);
|
|
25
26
|
const contentRef = React.useRef(null);
|
|
26
27
|
const { lockScroll, unlockScroll } = useScrollLock({ containerRef });
|
|
@@ -125,7 +126,8 @@ const Overlay = (props) => {
|
|
|
125
126
|
setOpacity: (value) => {
|
|
126
127
|
if (transparent)
|
|
127
128
|
return;
|
|
128
|
-
|
|
129
|
+
// Applied directly to the backdrop element to keep the style recalculation scoped to it
|
|
130
|
+
backdropRef.current?.style.setProperty("opacity", `${value}`);
|
|
129
131
|
},
|
|
130
132
|
}), [transparent]);
|
|
131
133
|
if (!rendered || !mounted)
|
|
@@ -136,10 +138,9 @@ const Overlay = (props) => {
|
|
|
136
138
|
}, style: {
|
|
137
139
|
"--rs-overlay-offset-x": containerRef ? `${offset[0]}px` : undefined,
|
|
138
140
|
"--rs-overlay-offset-y": containerRef ? `${offset[1]}px` : undefined,
|
|
139
|
-
"--rs-overlay-opacity": transparent ? 0 : undefined,
|
|
140
141
|
},
|
|
141
142
|
// oxlint-disable-next-line jsx_a11y/prefer-tag-over-role
|
|
142
|
-
role: "button", tabIndex: -1, className: rootClassNames, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onTransitionEnd: handleTransitionEnd, children:
|
|
143
|
+
role: "button", tabIndex: -1, className: rootClassNames, onMouseDown: handleMouseDown, onMouseUp: handleMouseUp, onTransitionEnd: handleTransitionEnd, children: _jsxs("div", { className: s.wrapper, children: [_jsx("div", { className: s.backdrop, ref: backdropRef }), _jsx("div", { className: s.inner, children: _jsx("div", { className: s.content, ref: contentRef, children: typeof children === "function" ? children({ active: visible }) : children }) })] }) })) }) }));
|
|
143
144
|
};
|
|
144
145
|
Overlay.displayName = "Overlay";
|
|
145
146
|
export default Overlay;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.root{--rs-overlay-offset-y:0px;--rs-overlay-offset-x:0px
|
|
1
|
+
.root{--rs-overlay-offset-y:0px;--rs-overlay-offset-x:0px;position:fixed;overflow-x:clip;-webkit-overflow-scrolling:touch;left:var(--rs-overlay-offset-x);top:var(--rs-overlay-offset-y);z-index:var(--rs-z-index-fixed);color:var(--rs-color-white);opacity:0;outline:none;isolation:isolate}.root,.wrapper{height:100%;width:100%}.wrapper{display:table;position:relative}.backdrop{position:absolute;inset:0;z-index:-1;background-color:rgb(from var(--rs-color-black) r g b/70%);opacity:0;will-change:opacity}.inner{display:table-cell;text-align:center}.content,.inner{vertical-align:middle}.content{display:inline-block;text-align:initial}.root.--visible,.root.--visible .backdrop{opacity:1}.root.--click-through{color:inherit;pointer-events:none}.root.--click-through .backdrop{opacity:0}.root.--blurred{backdrop-filter:blur(3px);left:calc(var(--rs-overlay-offset-x) - 1px);top:calc(var(--rs-overlay-offset-y) - 1px);width:calc(100% + 2px);height:calc(100% + 2px)}.root.--click-through .content,.root.--click-through>:not(.wrapper){pointer-events:all}.root.--contained{position:absolute}.root.--animated,.root.--animated .backdrop{transition:opacity var(--rs-duration-medium) var(--rs-easing-accelerate)}.root.--animated.--visible,.root.--animated.--visible .backdrop{transition-timing-function:var(--rs-easing-decelerate)}.root.--overflow-auto{overflow:auto;overscroll-behavior:none}
|
|
@@ -8,7 +8,7 @@ import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect";
|
|
|
8
8
|
import { resolveMixin } from "../../styles/mixin";
|
|
9
9
|
import s from "./ScrollArea.module.css";
|
|
10
10
|
const ScrollAreaBar = (props) => {
|
|
11
|
-
const { ratio,
|
|
11
|
+
const { ratio, vertical, scrollableRef, onThumbMove } = props;
|
|
12
12
|
const onThumbMoveRef = useHandlerRef(onThumbMove);
|
|
13
13
|
const [dragging, setDragging] = React.useState(false);
|
|
14
14
|
const dragStartPositionRef = React.useRef(0);
|
|
@@ -49,6 +49,31 @@ const ScrollAreaBar = (props) => {
|
|
|
49
49
|
setDragging(false);
|
|
50
50
|
enableScroll();
|
|
51
51
|
}, []);
|
|
52
|
+
/**
|
|
53
|
+
* Thumb position is written directly to a css variable on the bar instead of the react state,
|
|
54
|
+
* so scrolling doesn't re-render the component and the style recalculation stays scoped to the bar
|
|
55
|
+
*/
|
|
56
|
+
useIsomorphicLayoutEffect(() => {
|
|
57
|
+
const scrollableEl = scrollableRef.current;
|
|
58
|
+
const barEl = barRef.current;
|
|
59
|
+
if (!scrollableEl || !barEl)
|
|
60
|
+
return;
|
|
61
|
+
const updatePosition = () => {
|
|
62
|
+
const { scrollLeft, scrollTop, clientWidth, clientHeight, scrollWidth, scrollHeight } = scrollableEl;
|
|
63
|
+
const position = vertical
|
|
64
|
+
? scrollHeight <= clientHeight
|
|
65
|
+
? 0
|
|
66
|
+
: scrollTop / scrollHeight
|
|
67
|
+
: scrollWidth <= clientWidth
|
|
68
|
+
? 0
|
|
69
|
+
: scrollLeft / scrollWidth;
|
|
70
|
+
barEl.style.setProperty("--rs-scroll-area-position", `${position}`);
|
|
71
|
+
};
|
|
72
|
+
updatePosition();
|
|
73
|
+
scrollableEl.addEventListener("scroll", updatePosition, { passive: true });
|
|
74
|
+
return () => scrollableEl.removeEventListener("scroll", updatePosition);
|
|
75
|
+
// Ratio is included to re-sync the position after the content or the container size changes
|
|
76
|
+
}, [vertical, scrollableRef, ratio]);
|
|
52
77
|
React.useEffect(() => {
|
|
53
78
|
if (!dragging)
|
|
54
79
|
return;
|
|
@@ -59,15 +84,11 @@ const ScrollAreaBar = (props) => {
|
|
|
59
84
|
document.removeEventListener("mouseup", handleDragEnd);
|
|
60
85
|
};
|
|
61
86
|
}, [handleDrag, handleDragEnd, dragging]);
|
|
62
|
-
return (_jsx("div", { className: barClassNames, style: {
|
|
63
|
-
"--rs-scroll-area-ratio": ratio,
|
|
64
|
-
"--rs-scroll-area-position": position,
|
|
65
|
-
}, ref: barRef, onClick: handleClick, onMouseDown: handleDragStart, "aria-hidden": "true", children: _jsx("div", { className: s.thumb }) }));
|
|
87
|
+
return (_jsx("div", { className: barClassNames, style: { "--rs-scroll-area-ratio": ratio }, ref: barRef, onClick: handleClick, onMouseDown: handleDragStart, "aria-hidden": "true", children: _jsx("div", { className: s.thumb }) }));
|
|
66
88
|
};
|
|
67
89
|
const ScrollArea = forwardRef((props, ref) => {
|
|
68
90
|
const { children, height, maxHeight, orientation = "both", scrollbarDisplay = "hover", overscrollBehavior = "auto", fade, onScroll, className, attributes, scrollableAttributes, scrollableClassName, } = props;
|
|
69
91
|
const [scrollRatio, setScrollRatio] = React.useState({ x: 1, y: 1 });
|
|
70
|
-
const [scrollPosition, setScrollPosition] = React.useState({ x: 0, y: 0 });
|
|
71
92
|
const [scrolling, setScrolling] = React.useState(false);
|
|
72
93
|
const scrollingTimeoutRef = React.useRef(undefined);
|
|
73
94
|
const scrollableRef = React.useRef(null);
|
|
@@ -80,15 +101,11 @@ const ScrollArea = forwardRef((props, ref) => {
|
|
|
80
101
|
const scrollableEl = scrollableRef.current;
|
|
81
102
|
if (!scrollableEl)
|
|
82
103
|
return;
|
|
83
|
-
const {
|
|
104
|
+
const { clientWidth, clientHeight, scrollWidth, scrollHeight } = scrollableEl;
|
|
84
105
|
setScrollRatio({
|
|
85
106
|
x: scrollWidth === 0 ? 1 : Math.min(clientWidth / scrollWidth, 1),
|
|
86
107
|
y: scrollHeight === 0 ? 1 : Math.min(clientHeight / scrollHeight, 1),
|
|
87
108
|
});
|
|
88
|
-
setScrollPosition({
|
|
89
|
-
x: scrollWidth <= clientWidth ? 0 : scrollLeft / scrollWidth,
|
|
90
|
-
y: scrollHeight <= clientHeight ? 0 : scrollTop / scrollHeight,
|
|
91
|
-
});
|
|
92
109
|
}, []);
|
|
93
110
|
const handleScroll = (e) => {
|
|
94
111
|
const { scrollLeft, scrollTop, clientWidth, clientHeight, scrollWidth, scrollHeight } = e.currentTarget;
|
|
@@ -97,10 +114,6 @@ const ScrollArea = forwardRef((props, ref) => {
|
|
|
97
114
|
clearTimeout(scrollingTimeoutRef.current);
|
|
98
115
|
scrollingTimeoutRef.current = setTimeout(() => setScrolling(false), 1000);
|
|
99
116
|
}
|
|
100
|
-
setScrollPosition({
|
|
101
|
-
x: scrollLeft / scrollWidth,
|
|
102
|
-
y: scrollTop / scrollHeight,
|
|
103
|
-
});
|
|
104
117
|
onScroll?.({
|
|
105
118
|
x: scrollWidth === clientWidth ? 0 : scrollLeft / (scrollWidth - clientWidth),
|
|
106
119
|
y: scrollHeight === clientHeight ? 0 : scrollTop / (scrollHeight - clientHeight),
|
|
@@ -161,7 +174,7 @@ const ScrollArea = forwardRef((props, ref) => {
|
|
|
161
174
|
mutationObserver.disconnect();
|
|
162
175
|
};
|
|
163
176
|
}, [updateScroll]);
|
|
164
|
-
return (_jsxs("div", { ...attributes, ref: rootRef, className: rootClassNames, style: mixinStyles.variables, children: [_jsx("div", { className: scrollableClassNames, ref: scrollableRef, onScroll: handleScroll, children: _jsx("div", { ...scrollableAttributes, className: classNames(s.content, scrollableClassName), ref: contentRef, children: children }) }), orientation !== "horizontal" && scrollRatio.y < 1 && scrollbarDisplay !== "hidden" && (_jsx(ScrollAreaBar, { vertical: true, onThumbMove: handleThumbYMove, ratio: scrollRatio.y,
|
|
177
|
+
return (_jsxs("div", { ...attributes, ref: rootRef, className: rootClassNames, style: mixinStyles.variables, children: [_jsx("div", { className: scrollableClassNames, ref: scrollableRef, onScroll: handleScroll, children: _jsx("div", { ...scrollableAttributes, className: classNames(s.content, scrollableClassName), ref: contentRef, children: children }) }), orientation !== "horizontal" && scrollRatio.y < 1 && scrollbarDisplay !== "hidden" && (_jsx(ScrollAreaBar, { vertical: true, onThumbMove: handleThumbYMove, ratio: scrollRatio.y, scrollableRef: scrollableRef })), orientation !== "vertical" && scrollRatio.x < 1 && scrollbarDisplay !== "hidden" && (_jsx(ScrollAreaBar, { onThumbMove: handleThumbXMove, ratio: scrollRatio.x, scrollableRef: scrollableRef }))] }));
|
|
165
178
|
});
|
|
166
179
|
ScrollArea.displayName = "ScrollArea";
|
|
167
180
|
export default ScrollArea;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
@property --rs-scroll-area-fade-block-start{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-block-end{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-inline-start{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-inline-end{syntax:"<length>";initial-value:0;inherits:false}.root{--rs-scroll-area-thumb-size:var(--rs-unit-x1);--rs-scroll-area-thumb-offset:var(--rs-unit-x1);--rs-scroll-area-fade-size:var(--rs-unit-x8);overflow:hidden;height:100%;position:relative}[data-rs-keyboard] .root:has(.scrollable:focus){outline:var(--rs-outline)}.scrollable{height:100%;position:relative;overflow:auto;scrollbar-width:none;outline:none;-webkit-overflow-scrolling:touch}.scrollable::-webkit-scrollbar{display:none;width:0;height:0}.--orientation-vertical{overflow-x:hidden}.--orientation-vertical>.content{max-width:100%}.--orientation-horizontal{overflow-y:hidden}.--orientation-horizontal>.content{max-height:100%}@keyframes fade-block-start{0%{--rs-scroll-area-fade-block-start:0px}to{--rs-scroll-area-fade-block-start:var(--rs-scroll-area-fade-size)}}@keyframes fade-block-end{0%{--rs-scroll-area-fade-block-end:var(--rs-scroll-area-fade-size)}to{--rs-scroll-area-fade-block-end:0px}}@keyframes fade-inline-start{0%{--rs-scroll-area-fade-inline-start:0px}to{--rs-scroll-area-fade-inline-start:var(--rs-scroll-area-fade-size)}}@keyframes fade-inline-end{0%{--rs-scroll-area-fade-inline-end:var(--rs-scroll-area-fade-size)}to{--rs-scroll-area-fade-inline-end:0px}}@supports (animation-timeline:scroll(self block)){.--fade{--rs-scroll-area-fade-inline-direction:to right;mask-image:linear-gradient(to bottom,transparent 0,rgba(0,0,0,.1) calc(var(--rs-scroll-area-fade-block-start) * .2),rgba(0,0,0,.35) calc(var(--rs-scroll-area-fade-block-start) * .4),rgba(0,0,0,.65) calc(var(--rs-scroll-area-fade-block-start) * .6),rgba(0,0,0,.9) calc(var(--rs-scroll-area-fade-block-start) * .8),#000 var(--rs-scroll-area-fade-block-start),#000 calc(100% - var(--rs-scroll-area-fade-block-end)),rgba(0,0,0,.9) calc(100% - var(--rs-scroll-area-fade-block-end) * .8),rgba(0,0,0,.65) calc(100% - var(--rs-scroll-area-fade-block-end) * .6),rgba(0,0,0,.35) calc(100% - var(--rs-scroll-area-fade-block-end) * .4),rgba(0,0,0,.1) calc(100% - var(--rs-scroll-area-fade-block-end) * .2),transparent 100%),linear-gradient(var(--rs-scroll-area-fade-inline-direction),transparent 0,rgba(0,0,0,.1) calc(var(--rs-scroll-area-fade-inline-start) * .2),rgba(0,0,0,.35) calc(var(--rs-scroll-area-fade-inline-start) * .4),rgba(0,0,0,.65) calc(var(--rs-scroll-area-fade-inline-start) * .6),rgba(0,0,0,.9) calc(var(--rs-scroll-area-fade-inline-start) * .8),#000 var(--rs-scroll-area-fade-inline-start),#000 calc(100% - var(--rs-scroll-area-fade-inline-end)),rgba(0,0,0,.9) calc(100% - var(--rs-scroll-area-fade-inline-end) * .8),rgba(0,0,0,.65) calc(100% - var(--rs-scroll-area-fade-inline-end) * .6),rgba(0,0,0,.35) calc(100% - var(--rs-scroll-area-fade-inline-end) * .4),rgba(0,0,0,.1) calc(100% - var(--rs-scroll-area-fade-inline-end) * .2),transparent 100%);mask-composite:intersect;animation-name:fade-block-start,fade-block-end,fade-inline-start,fade-inline-end;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(self block),scroll(self block),scroll(self inline),scroll(self inline);animation-range:0 var(--rs-scroll-area-fade-size),calc(100% - var(--rs-scroll-area-fade-size)) 100%,0 var(--rs-scroll-area-fade-size),calc(100% - var(--rs-scroll-area-fade-size)) 100%}[dir=rtl] .--fade{--rs-scroll-area-fade-inline-direction:to left}}.--overscroll-auto{overscroll-behavior:auto}.--overscroll-contain{overscroll-behavior:contain}.content{display:inline-block;vertical-align:top;min-width:100%;min-height:100%;height:100%}.scrollbar{position:absolute;box-sizing:initial;padding:var(--rs-scroll-area-thumb-offset)}.thumb{position:relative;height:100%;width:100%;background-color:var(--rs-color-background-neutral-faded);opacity:0}.thumb,.thumb:before{border-radius:var(--rs-radius-circular);transition:opacity var(--rs-duration-fast) var(--rs-easing-standard)}.thumb:before{content:"";display:block;background:var(--rs-color-foreground-neutral);position:absolute;opacity:.2}.--scrollbar-y{inset-block:0;inset-inline-end:0;width:var(--rs-scroll-area-thumb-size);padding-block:calc(var(--rs-scroll-area-thumb-offset) + var(--rs-unit-x1))}.--scrollbar-y .thumb:before{inset-block-start:calc(var(--rs-scroll-area-
|
|
1
|
+
@property --rs-scroll-area-fade-block-start{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-block-end{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-inline-start{syntax:"<length>";initial-value:0;inherits:false}@property --rs-scroll-area-fade-inline-end{syntax:"<length>";initial-value:0;inherits:false}.root{--rs-scroll-area-thumb-size:var(--rs-unit-x1);--rs-scroll-area-thumb-offset:var(--rs-unit-x1);--rs-scroll-area-fade-size:var(--rs-unit-x8);overflow:hidden;height:100%;position:relative}[data-rs-keyboard] .root:has(.scrollable:focus){outline:var(--rs-outline)}.scrollable{height:100%;position:relative;overflow:auto;scrollbar-width:none;outline:none;-webkit-overflow-scrolling:touch}.scrollable::-webkit-scrollbar{display:none;width:0;height:0}.--orientation-vertical{overflow-x:hidden}.--orientation-vertical>.content{max-width:100%}.--orientation-horizontal{overflow-y:hidden}.--orientation-horizontal>.content{max-height:100%}@keyframes fade-block-start{0%{--rs-scroll-area-fade-block-start:0px}to{--rs-scroll-area-fade-block-start:var(--rs-scroll-area-fade-size)}}@keyframes fade-block-end{0%{--rs-scroll-area-fade-block-end:var(--rs-scroll-area-fade-size)}to{--rs-scroll-area-fade-block-end:0px}}@keyframes fade-inline-start{0%{--rs-scroll-area-fade-inline-start:0px}to{--rs-scroll-area-fade-inline-start:var(--rs-scroll-area-fade-size)}}@keyframes fade-inline-end{0%{--rs-scroll-area-fade-inline-end:var(--rs-scroll-area-fade-size)}to{--rs-scroll-area-fade-inline-end:0px}}@supports (animation-timeline:scroll(self block)){.--fade{--rs-scroll-area-fade-inline-direction:to right;mask-image:linear-gradient(to bottom,transparent 0,rgba(0,0,0,.1) calc(var(--rs-scroll-area-fade-block-start) * .2),rgba(0,0,0,.35) calc(var(--rs-scroll-area-fade-block-start) * .4),rgba(0,0,0,.65) calc(var(--rs-scroll-area-fade-block-start) * .6),rgba(0,0,0,.9) calc(var(--rs-scroll-area-fade-block-start) * .8),#000 var(--rs-scroll-area-fade-block-start),#000 calc(100% - var(--rs-scroll-area-fade-block-end)),rgba(0,0,0,.9) calc(100% - var(--rs-scroll-area-fade-block-end) * .8),rgba(0,0,0,.65) calc(100% - var(--rs-scroll-area-fade-block-end) * .6),rgba(0,0,0,.35) calc(100% - var(--rs-scroll-area-fade-block-end) * .4),rgba(0,0,0,.1) calc(100% - var(--rs-scroll-area-fade-block-end) * .2),transparent 100%),linear-gradient(var(--rs-scroll-area-fade-inline-direction),transparent 0,rgba(0,0,0,.1) calc(var(--rs-scroll-area-fade-inline-start) * .2),rgba(0,0,0,.35) calc(var(--rs-scroll-area-fade-inline-start) * .4),rgba(0,0,0,.65) calc(var(--rs-scroll-area-fade-inline-start) * .6),rgba(0,0,0,.9) calc(var(--rs-scroll-area-fade-inline-start) * .8),#000 var(--rs-scroll-area-fade-inline-start),#000 calc(100% - var(--rs-scroll-area-fade-inline-end)),rgba(0,0,0,.9) calc(100% - var(--rs-scroll-area-fade-inline-end) * .8),rgba(0,0,0,.65) calc(100% - var(--rs-scroll-area-fade-inline-end) * .6),rgba(0,0,0,.35) calc(100% - var(--rs-scroll-area-fade-inline-end) * .4),rgba(0,0,0,.1) calc(100% - var(--rs-scroll-area-fade-inline-end) * .2),transparent 100%);mask-composite:intersect;animation-name:fade-block-start,fade-block-end,fade-inline-start,fade-inline-end;animation-timing-function:linear;animation-fill-mode:both;animation-timeline:scroll(self block),scroll(self block),scroll(self inline),scroll(self inline);animation-range:0 var(--rs-scroll-area-fade-size),calc(100% - var(--rs-scroll-area-fade-size)) 100%,0 var(--rs-scroll-area-fade-size),calc(100% - var(--rs-scroll-area-fade-size)) 100%}[dir=rtl] .--fade{--rs-scroll-area-fade-inline-direction:to left}}.--overscroll-auto{overscroll-behavior:auto}.--overscroll-contain{overscroll-behavior:contain}.content{display:inline-block;vertical-align:top;min-width:100%;min-height:100%;height:100%}.scrollbar{position:absolute;box-sizing:initial;padding:var(--rs-scroll-area-thumb-offset)}.thumb{position:relative;height:100%;width:100%;background-color:var(--rs-color-background-neutral-faded);opacity:0}.thumb,.thumb:before{border-radius:var(--rs-radius-circular);transition:opacity var(--rs-duration-fast) var(--rs-easing-standard)}.thumb:before{content:"";display:block;background:var(--rs-color-foreground-neutral);position:absolute;opacity:.2;will-change:transform}.--scrollbar-y{inset-block:0;inset-inline-end:0;width:var(--rs-scroll-area-thumb-size);padding-block:calc(var(--rs-scroll-area-thumb-offset) + var(--rs-unit-x1))}.--scrollbar-y .thumb:before{inset-block-start:0;width:100%;height:calc(var(--rs-scroll-area-ratio) * 100%);transform:translateY(calc(var(--rs-scroll-area-position, 0) / var(--rs-scroll-area-ratio) * 100%))}.--scrollbar-x{inset-inline:0;inset-block-end:0;height:var(--rs-scroll-area-thumb-size);padding-inline:calc(var(--rs-scroll-area-thumb-offset) + var(--rs-unit-x0-5))}.--scrollbar-x .thumb:before{inset-inline-start:0;height:100%;width:calc(var(--rs-scroll-area-ratio) * 100%);transform:translateX(calc(var(--rs-scroll-area-position, 0) / var(--rs-scroll-area-ratio) * 100%))}.--scrollbar-y:not(:last-child){inset-block-end:calc(var(--rs-scroll-area-thumb-size) + var(--rs-scroll-area-thumb-offset) * 2)}.--scrollbar-y~.--scrollbar-x{inset-inline-end:calc(var(--rs-scroll-area-thumb-size) + var(--rs-scroll-area-thumb-offset) * 2)}.--display-hover:hover>.scrollbar .thumb,.--display-hover>.--scrollbar-dragging .thumb,.--display-scroll.--scrolling>.scrollbar .thumb,.--display-scroll>.--scrollbar-dragging .thumb,.--display-visible>.scrollbar .thumb{opacity:1}.--display-hover:hover .--scrollbar-dragging .thumb:before,.--scrollbar-dragging .thumb:before,.scrollbar:hover .thumb:before{opacity:.32}
|
|
@@ -124,7 +124,7 @@ const SelectCustomControlled = (props) => {
|
|
|
124
124
|
return passedRenderTrigger(attributes, { ...triggerState, value });
|
|
125
125
|
return passedRenderTrigger(attributes, { ...triggerState, value });
|
|
126
126
|
};
|
|
127
|
-
return (_jsxs(DropdownMenu, { width: width,
|
|
127
|
+
return (_jsxs(DropdownMenu, { width: width, position: position ?? "bottom", fallbackPositions: fallbackPositions ?? (position ? undefined : ["bottom", "top"]), fallbackAdjustLayout: !openedWithTouch, fallbackMinHeight: "220px", initialFocusRef: initialFocusRef, positionRef: positionRef, onClose: () => setOpenedWithTouch(false), children: [_jsx(DropdownMenu.Trigger, { children: (attributes) => {
|
|
128
128
|
const triggerAttributes = {
|
|
129
129
|
...attributes,
|
|
130
130
|
onTouchStart: () => {
|
|
@@ -99,6 +99,10 @@ exports.base = {
|
|
|
99
99
|
(0, test_1.expect)(options[0]).toHaveTextContent("Dog");
|
|
100
100
|
(0, test_1.expect)(options[1]).toHaveTextContent("Turtle");
|
|
101
101
|
await test_1.userEvent.click(document.body);
|
|
102
|
+
// Wait for the previous menu to finish its hide animation
|
|
103
|
+
await (0, test_1.waitFor)(() => {
|
|
104
|
+
(0, test_1.expect)((0, test_1.within)(canvasElement.ownerDocument.body).queryAllByRole("option")).toHaveLength(0);
|
|
105
|
+
});
|
|
102
106
|
const [_, triggerWithGroups] = canvas.getAllByRole("button");
|
|
103
107
|
const hiddenInputs2 = canvasElement.querySelectorAll('input[type="hidden"]');
|
|
104
108
|
const [__, hiddenInputWithGroups] = Array.from(hiddenInputs2);
|
|
@@ -345,7 +349,10 @@ exports.renderTrigger = {
|
|
|
345
349
|
await test_1.userEvent.click(option);
|
|
346
350
|
(0, test_1.expect)(hiddenInput).toHaveValue("turtle");
|
|
347
351
|
(0, test_1.expect)(trigger).toHaveTextContent("Turtle");
|
|
348
|
-
|
|
352
|
+
// Trigger stays expanded until the hide animation ends
|
|
353
|
+
await (0, test_1.waitFor)(() => {
|
|
354
|
+
(0, test_1.expect)(trigger).toHaveAttribute("aria-expanded", "false");
|
|
355
|
+
});
|
|
349
356
|
(0, test_1.expect)(args.handleChange).toHaveBeenCalledTimes(1);
|
|
350
357
|
(0, test_1.expect)(args.handleChange).toHaveBeenCalledWith({
|
|
351
358
|
name: "animal",
|
|
@@ -616,6 +623,10 @@ exports.defaultHandlers = {
|
|
|
616
623
|
});
|
|
617
624
|
// Controlled
|
|
618
625
|
await test_1.userEvent.click(document.body);
|
|
626
|
+
// Wait for the previous menu to finish its hide animation
|
|
627
|
+
await (0, test_1.waitFor)(() => {
|
|
628
|
+
(0, test_1.expect)((0, test_1.within)(canvasElement.ownerDocument.body).queryAllByRole("option")).toHaveLength(0);
|
|
629
|
+
});
|
|
619
630
|
const [__, controlled, focusable] = canvas.getAllByRole("button");
|
|
620
631
|
const hiddenInputs2 = canvasElement.querySelectorAll('input[type="hidden"]');
|
|
621
632
|
const [___, inputControlled] = Array.from(hiddenInputs2);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import s from "./Freeze.module.css";
|
|
5
|
+
/**
|
|
6
|
+
* Transitions never commit an already visible Suspense boundary switching to its fallback,
|
|
7
|
+
* so we stop freezing if the frozen render isn't committed in time instead of blocking them forever
|
|
8
|
+
*/
|
|
9
|
+
const COMMIT_TIMEOUT = 100;
|
|
10
|
+
const infinitePromise = new Promise(() => { });
|
|
11
|
+
const Suspend = (props) => {
|
|
12
|
+
const state = props.stateRef.current;
|
|
13
|
+
if (state.expired)
|
|
14
|
+
return null;
|
|
15
|
+
if (state.committed)
|
|
16
|
+
throw infinitePromise;
|
|
17
|
+
state.pending ??= new Promise((resolve) => {
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
state.expired = !state.committed;
|
|
20
|
+
resolve();
|
|
21
|
+
}, COMMIT_TIMEOUT);
|
|
22
|
+
});
|
|
23
|
+
throw state.pending;
|
|
24
|
+
};
|
|
25
|
+
const Freeze = (props) => {
|
|
26
|
+
const { frozen, children } = props;
|
|
27
|
+
const stateRef = React.useRef({});
|
|
28
|
+
const elRef = React.useRef(null);
|
|
29
|
+
// Insertion effect is the earliest opportunity to undo the Suspense display: none
|
|
30
|
+
React.useInsertionEffect(() => {
|
|
31
|
+
if (!frozen) {
|
|
32
|
+
stateRef.current = {};
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (stateRef.current.expired)
|
|
36
|
+
return;
|
|
37
|
+
stateRef.current.committed = true;
|
|
38
|
+
elRef.current?.style.removeProperty("display");
|
|
39
|
+
}, [frozen]);
|
|
40
|
+
return (_jsx(React.Suspense, { children: _jsxs("div", { className: s.root, ref: (el) => {
|
|
41
|
+
if (el)
|
|
42
|
+
elRef.current = el;
|
|
43
|
+
}, children: [frozen && _jsx(Suspend, { stateRef: stateRef }), children] }) }));
|
|
44
|
+
};
|
|
45
|
+
Freeze.displayName = "Freeze";
|
|
46
|
+
export default Freeze;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.root{display:contents}
|