From f1ea4f33956c068ceeb49da44b2d3924153dd716 Mon Sep 17 00:00:00 2001 From: Jack Shelton <104264123+thejackshelton@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:53:10 -0500 Subject: [PATCH] feat: migrate the carousel to transforms (#963) * feat: get custom scroll for mouse and update * refactor: remove drag speed * refactor: add back drag speed * feat: one scroll model * fix: clean up a bit * fix: dumb timeouts * without animateScroll$ * does transform walking * feat: correct drag direction * initial snapping * correct snapping * works with next and prev * add will change transform * custom animations except for initial load * user defined transitions work * fix: resize * mobile sorta works * smoother on mobile * fix: simplify scroller impl * narrowed down problem * mobile working * handle x, y, or z * working in an agnostic way * cleanup markup and props * get initial slide position * refactor: add comment on qvisible * feat: no flicker getting initial slide * teeny flicker but with animations * feat: no more flicker * feat: handle initial slide pos for any orientation * feat: respect transform boundaries * fix: carousel start index * fix: progress * fix: make sure there are no duplicate handler calls * fix: respect boundaries * refactor: make carousel scroller easier to understand * fix: remove the viewport from the equation * fix: smoother swiping experience * feat: sensitivity * feat: initial move impl * fix: only calculate pos when it's not the start * feat: renders correct bullets based on slides per view * feat: proper bullet navigation * feat: bullets are smart enough to know if something is in range * feat: prev and next buttons both work with move * fix: state tests * feat: initial vertical * fix: offset for vertical version * feat: vertical with transform * fix: touch start moves according to touchY in vertical * refactor: better naming * refactor: code more easy to understand * refactored names * refactor: use props map instead * fix: initial slide position vertically * fix: don't render marker points with non-scroller carousels * feat: start docs * fix: text align * docs: add example animation * feat: upgrade carousel to beta * fix: the merge issues --- apps/website/src/_state/component-statuses.ts | 2 +- .../components/feature-list/feature-list.tsx | 2 +- .../src/routes/docs/contributing/index.mdx | 10 +- .../headless/carousel/examples/animate.tsx | 26 ++ .../headless/carousel/examples/carousel.css | 13 +- .../docs/headless/carousel/examples/move.tsx | 52 +++ .../headless/carousel/examples/reactive.tsx | 2 +- .../examples/{loop.tsx => rewind.tsx} | 2 +- .../carousel/examples/sensitivity.tsx | 33 ++ .../docs/headless/carousel/examples/start.tsx | 30 ++ .../carousel/examples/vertical-direction.tsx | 7 +- .../routes/docs/headless/carousel/index.mdx | 160 ++++--- .../src/components/carousel/bullet.tsx | 56 ++- .../src/components/carousel/carousel.css | 75 +-- .../src/components/carousel/carousel.test.ts | 436 +++++++++--------- .../src/components/carousel/context.ts | 10 +- .../src/components/carousel/next.tsx | 30 +- .../src/components/carousel/previous.tsx | 45 +- .../src/components/carousel/root.tsx | 251 +++++----- .../src/components/carousel/scroller.tsx | 293 ++++++------ .../src/components/carousel/slide.tsx | 44 +- .../src/components/carousel/use-carousel.tsx | 24 +- .../src/components/carousel/use-scroller.tsx | 156 +++++++ .../kit-headless/src/utils/bound-signal.tsx | 22 +- 24 files changed, 1146 insertions(+), 635 deletions(-) create mode 100644 apps/website/src/routes/docs/headless/carousel/examples/animate.tsx create mode 100644 apps/website/src/routes/docs/headless/carousel/examples/move.tsx rename apps/website/src/routes/docs/headless/carousel/examples/{loop.tsx => rewind.tsx} (94%) create mode 100644 apps/website/src/routes/docs/headless/carousel/examples/sensitivity.tsx create mode 100644 apps/website/src/routes/docs/headless/carousel/examples/start.tsx create mode 100644 packages/kit-headless/src/components/carousel/use-scroller.tsx diff --git a/apps/website/src/_state/component-statuses.ts b/apps/website/src/_state/component-statuses.ts index 93da6bd5c..d1fdf99d7 100644 --- a/apps/website/src/_state/component-statuses.ts +++ b/apps/website/src/_state/component-statuses.ts @@ -38,7 +38,7 @@ export const statusByComponent: ComponentKitsStatuses = { }, headless: { Accordion: ComponentStatus.Beta, - Carousel: ComponentStatus.Draft, + Carousel: ComponentStatus.Beta, Collapsible: ComponentStatus.Beta, Combobox: ComponentStatus.Beta, Checkbox: ComponentStatus.Draft, diff --git a/apps/website/src/components/feature-list/feature-list.tsx b/apps/website/src/components/feature-list/feature-list.tsx index 87bce4830..6e276138c 100644 --- a/apps/website/src/components/feature-list/feature-list.tsx +++ b/apps/website/src/components/feature-list/feature-list.tsx @@ -68,7 +68,7 @@ export const FeatureList = component$((props: FeatureListProps) => { {!props.issues && ( Missing a feature? Check out the{' '} - + contributing guide {' '} and we'd be happy to review any relevant issues or PR's. Feel free to work on diff --git a/apps/website/src/routes/docs/contributing/index.mdx b/apps/website/src/routes/docs/contributing/index.mdx index 05ee6f3c3..d90b6739a 100644 --- a/apps/website/src/routes/docs/contributing/index.mdx +++ b/apps/website/src/routes/docs/contributing/index.mdx @@ -379,11 +379,7 @@ Notice how `` returns a `children` prop. This is because inline c Context and hooks is still easy to use, create a new component called `` and return that instead of the div (with the children passed between) in the example above. From there, you can use context, hooks, and all the other Qwik goodies as a top level component. ```tsx -return ( - - {props.children} - -) +return {props.children}; // use hooks, context, and other stuff here! export const ExampleBase = component$(() => { @@ -391,8 +387,8 @@ export const ExampleBase = component$(() => {
- ) -}) + ); +}); ``` ## That's it! diff --git a/apps/website/src/routes/docs/headless/carousel/examples/animate.tsx b/apps/website/src/routes/docs/headless/carousel/examples/animate.tsx new file mode 100644 index 000000000..7c634a5d7 --- /dev/null +++ b/apps/website/src/routes/docs/headless/carousel/examples/animate.tsx @@ -0,0 +1,26 @@ +import { component$, useStyles$ } from '@builder.io/qwik'; +import { Carousel } from '@qwik-ui/headless'; + +export default component$(() => { + useStyles$(styles); + + const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink']; + + return ( + + + + {colors.map((color) => ( + + {color} + + ))} + + + ); +}); +// internal +import styles from './carousel.css?inline'; diff --git a/apps/website/src/routes/docs/headless/carousel/examples/carousel.css b/apps/website/src/routes/docs/headless/carousel/examples/carousel.css index ce8bc10a9..b39762a16 100644 --- a/apps/website/src/routes/docs/headless/carousel/examples/carousel.css +++ b/apps/website/src/routes/docs/headless/carousel/examples/carousel.css @@ -2,13 +2,10 @@ width: 100%; } -.carousel-scroller { - margin-bottom: 0.5rem; -} - .carousel-slide { border: 2px dotted hsl(var(--primary)); min-height: 10rem; + max-height: 10rem; -webkit-user-select: none; /* support for Safari */ user-select: none; } @@ -18,6 +15,7 @@ gap: 0.5rem; padding: 1rem; border: 2px dotted hsl(var(--foreground)); + margin-top: 0.5rem; } .carousel-buttons { @@ -25,6 +23,7 @@ justify-content: space-between; border: 2px dotted hsl(var(--accent)); margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } .carousel-buttons button { @@ -76,6 +75,8 @@ .carousel-stepper { display: flex; justify-content: space-between; + margin-bottom: 0.5rem; + flex-wrap: wrap; } .carousel-step { @@ -100,3 +101,7 @@ .carousel-step[data-current]::before { background-color: hsl(var(--primary)); } + +.carousel-animation { + transition: 0.35s transform cubic-bezier(0.57, 0.16, 0.95, 0.67); +} diff --git a/apps/website/src/routes/docs/headless/carousel/examples/move.tsx b/apps/website/src/routes/docs/headless/carousel/examples/move.tsx new file mode 100644 index 000000000..38accb7cb --- /dev/null +++ b/apps/website/src/routes/docs/headless/carousel/examples/move.tsx @@ -0,0 +1,52 @@ +import { component$, useStyles$ } from '@builder.io/qwik'; +import { Carousel } from '@qwik-ui/headless'; + +export default component$(() => { + useStyles$(styles); + + const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink']; + + useStyles$(` + .carousel-circle { + width: 20px; + height: 20px; + margin: 0 5px; + border-radius: 50%; + background-color: lightgray; + } + + .carousel-circle[data-active] { + background-color: lightblue; + } + `); + + return ( + <> + + + + {colors.map((color) => ( + + {color} + + ))} + + + + {colors.map((_, index) => { + return ( + + ); + })} + + + + ); +}); +// internal +import styles from './carousel.css?inline'; diff --git a/apps/website/src/routes/docs/headless/carousel/examples/reactive.tsx b/apps/website/src/routes/docs/headless/carousel/examples/reactive.tsx index b05735d6b..38ed1a7a0 100644 --- a/apps/website/src/routes/docs/headless/carousel/examples/reactive.tsx +++ b/apps/website/src/routes/docs/headless/carousel/examples/reactive.tsx @@ -5,7 +5,7 @@ export default component$(() => { useStyles$(styles); const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink']; - const selectedIndex = useSignal(0); + const selectedIndex = useSignal(2); return ( <> diff --git a/apps/website/src/routes/docs/headless/carousel/examples/loop.tsx b/apps/website/src/routes/docs/headless/carousel/examples/rewind.tsx similarity index 94% rename from apps/website/src/routes/docs/headless/carousel/examples/loop.tsx rename to apps/website/src/routes/docs/headless/carousel/examples/rewind.tsx index 23b4588ca..10bed5d21 100644 --- a/apps/website/src/routes/docs/headless/carousel/examples/loop.tsx +++ b/apps/website/src/routes/docs/headless/carousel/examples/rewind.tsx @@ -7,7 +7,7 @@ export default component$(() => { const colors = ['red', 'green', 'blue', 'yellow', 'purple', 'orange', 'pink']; return ( - +