From fa1009ec90519108cc80fd938862749c9995ead8 Mon Sep 17 00:00:00 2001 From: jack shelton Date: Sun, 29 Sep 2024 05:47:46 -0500 Subject: [PATCH 1/3] feat: carousel now uses auto api --- apps/website/auto-api.ts | 1 - .../src/components/api-table/auto-api.tsx | 14 +- .../docs/headless/carousel/auto-api/api.ts | 122 ++++++++++++++++++ .../routes/docs/headless/carousel/index.mdx | 69 +--------- .../src/routes/docs/headless/select/index.mdx | 1 - .../src/components/carousel/inline.tsx | 6 +- .../src/components/carousel/root.tsx | 4 +- 7 files changed, 141 insertions(+), 76 deletions(-) create mode 100644 apps/website/src/routes/docs/headless/carousel/auto-api/api.ts diff --git a/apps/website/auto-api.ts b/apps/website/auto-api.ts index d4f2bafe2..0d9442c8d 100644 --- a/apps/website/auto-api.ts +++ b/apps/website/auto-api.ts @@ -1,6 +1,5 @@ import * as fs from 'fs'; import { resolve } from 'path'; -import { inspect } from 'util'; import { ViteDevServer } from 'vite'; export default function autoAPI() { return { diff --git a/apps/website/src/components/api-table/auto-api.tsx b/apps/website/src/components/api-table/auto-api.tsx index 526b6b33c..b216be717 100644 --- a/apps/website/src/components/api-table/auto-api.tsx +++ b/apps/website/src/components/api-table/auto-api.tsx @@ -1,6 +1,5 @@ import { JSXOutput, component$, $, QRL, useTask$, useSignal } from '@builder.io/qwik'; import { APITable, type APITableProps } from './api-table'; -import { packages } from '../install-snippet/install-snippet'; //This is a workaround for not being able to export across packages due to nx rule: // https://nx.dev/features/enforce-module-boundaries#enforce-module-boundaries @@ -32,7 +31,7 @@ type ParsedCommentsProps = { parsedProps: PublicType; config: AutoAPIConfig; }; -const currentHeader = $((_: string) => { +const currentHeader = $(() => { //cannot send h2 from here because current TOC can only read md return null; }); @@ -75,8 +74,8 @@ export const AutoAPI = component$( return ( <> {topHeaderSig.value} - {subComponents.map((e) => ( - + {subComponents.map((e, index) => ( + ))} ); @@ -109,10 +108,15 @@ const ParsedComments = component$(({ parsedProps, config }) useTask$(async () => { const translation: APITableProps = { propDescriptors: parsedProps[key].map((e) => { + const isObject = e.type.includes('{'); + const isUnion = e.type.includes('|'); + const isPopup = isObject || isUnion; + return { name: e.prop, - type: e.type, + type: isObject ? 'object' : isUnion ? 'union' : e.type, description: e.comment, + info: (isPopup && e.type) || undefined, }; }), }; diff --git a/apps/website/src/routes/docs/headless/carousel/auto-api/api.ts b/apps/website/src/routes/docs/headless/carousel/auto-api/api.ts new file mode 100644 index 000000000..473dbd7e0 --- /dev/null +++ b/apps/website/src/routes/docs/headless/carousel/auto-api/api.ts @@ -0,0 +1,122 @@ +export const api = { + carousel: [ + { + bullet: [], + }, + { + inline: [], + }, + { + next: [], + }, + { + pagination: [], + }, + { + player: [], + }, + { + previous: [], + }, + { + root: [ + { + CarouselRootProps: [ + { + comment: 'The gap between slides', + prop: 'gap?', + type: 'number', + }, + { + comment: 'Number of slides to show at once', + prop: 'slidesPerView?', + type: 'number', + }, + { + comment: 'Whether the carousel is draggable', + prop: 'draggable?', + type: 'boolean', + }, + { + comment: 'Alignment of slides within the viewport', + prop: 'align?', + type: "'start' | 'center' | 'end'", + }, + { + comment: 'Whether the carousel should rewind', + prop: 'rewind?', + type: 'boolean', + }, + { + comment: 'Bind the selected index to a signal', + prop: "'bind:selectedIndex'?", + type: 'Signal', + }, + { + comment: 'change the initial index of the carousel on render', + prop: 'startIndex?', + type: 'number', + }, + { + comment: + '@deprecated Use bind:selectedIndex instead\n Bind the current slide index to a signal', + prop: "'bind:currSlideIndex'?", + type: 'Signal', + }, + { + comment: 'Whether the carousel should autoplay', + prop: "'bind:autoplay'?", + type: 'Signal', + }, + { + comment: 'the current progress of the carousel', + prop: "'bind:progress'?", + type: 'Signal', + }, + { + comment: 'Time in milliseconds before the next slide plays during autoplay', + prop: 'autoPlayIntervalMs?', + type: 'number', + }, + { + comment: '@internal Total number of slides', + prop: '_numSlides?', + type: 'number', + }, + { + comment: '@internal Whether this carousel has a title', + prop: '_isTitle?', + type: 'boolean', + }, + { + comment: 'The sensitivity of the carousel dragging', + prop: 'sensitivity?', + type: '{', + }, + ], + }, + ], + }, + { + scroller: [], + }, + { + slide: [], + }, + { + step: [], + }, + { + stepper: [], + }, + { + title: [], + }, + { + 'use-carousel': [], + }, + { + 'use-scroller': [], + }, + ], +}; diff --git a/apps/website/src/routes/docs/headless/carousel/index.mdx b/apps/website/src/routes/docs/headless/carousel/index.mdx index a0e32da50..a0449ce6f 100644 --- a/apps/website/src/routes/docs/headless/carousel/index.mdx +++ b/apps/website/src/routes/docs/headless/carousel/index.mdx @@ -4,6 +4,10 @@ import { FeatureList } from '~/components/feature-list/feature-list'; import { Note } from '~/components/note/note'; +import { AutoAPI } from '~/components/api-table/auto-api'; + +import { api } from './auto-api/api'; + # Carousel @@ -327,67 +331,4 @@ In the above example, we also use the headless progress component to show the pr ## API -### Carousel.Root - -', - description: 'Bind the selected index to a signal.', - }, - { - name: 'startIndex', - type: 'number', - description: 'Change the initial index of the carousel on render.', - }, - { - name: 'bind:autoplay', - type: 'Signal', - description: 'Whether the carousel should autoplay.', - }, - { - name: 'autoPlayIntervalMs', - type: 'number', - description: 'Time in milliseconds before the next slide plays during autoplay.', - }, - { - name: 'direction', - type: 'union', - description: - 'Change the direction of the carousel, for it to be veritical define the maxSlideHeight prop as well.', - info: '"row" | "column"', - }, - { - name: 'maxSlideHeight', - type: 'number', - description: 'Write the height of the longest slide.', - }, - ]} -/> + diff --git a/apps/website/src/routes/docs/headless/select/index.mdx b/apps/website/src/routes/docs/headless/select/index.mdx index d92d0c4bc..d42ead181 100644 --- a/apps/website/src/routes/docs/headless/select/index.mdx +++ b/apps/website/src/routes/docs/headless/select/index.mdx @@ -2,7 +2,6 @@ title: Qwik UI | Select --- -import { api } from './auto-api/api'; import { FeatureList } from '~/components/feature-list/feature-list'; import { statusByComponent } from '~/_state/component-statuses'; diff --git a/packages/kit-headless/src/components/carousel/inline.tsx b/packages/kit-headless/src/components/carousel/inline.tsx index 80a804380..8c2d3bdcf 100644 --- a/packages/kit-headless/src/components/carousel/inline.tsx +++ b/packages/kit-headless/src/components/carousel/inline.tsx @@ -1,5 +1,5 @@ import { Component } from '@builder.io/qwik'; -import { CarouselBase, CarouselRootProps } from './root'; +import { CarouselBase, PublicCarouselRootProps } from './root'; import { Carousel } from '@qwik-ui/headless'; import { findComponent, processChildren } from '../../utils/inline-component'; @@ -20,8 +20,8 @@ type InternalProps = { titleComponent?: typeof Carousel.Title; }; -export const CarouselRoot: Component = ( - props: CarouselRootProps & InternalProps, +export const CarouselRoot: Component = ( + props: PublicCarouselRootProps & InternalProps, ) => { const { children, diff --git a/packages/kit-headless/src/components/carousel/root.tsx b/packages/kit-headless/src/components/carousel/root.tsx index 720060dea..7a9209330 100644 --- a/packages/kit-headless/src/components/carousel/root.tsx +++ b/packages/kit-headless/src/components/carousel/root.tsx @@ -13,7 +13,7 @@ import { CarouselContext, carouselContextId } from './context'; import { useBoundSignal } from '../../utils/bound-signal'; import { useAutoplay } from './use-carousel'; -export type CarouselRootProps = PropsOf<'div'> & { +export type PublicCarouselRootProps = PropsOf<'div'> & { /** The gap between slides */ gap?: number; @@ -72,7 +72,7 @@ export type CarouselRootProps = PropsOf<'div'> & { maxSlideHeight?: number; }; -export const CarouselBase = component$((props: CarouselRootProps) => { +export const CarouselBase = component$((props: PublicCarouselRootProps) => { const { align, 'bind:currSlideIndex': givenOldSlideIndexSig, From 649eb577a796316e64b90571b32bf47659bd0991 Mon Sep 17 00:00:00 2001 From: jack shelton Date: Sun, 29 Sep 2024 05:52:42 -0500 Subject: [PATCH 2/3] fix: rollup warning to properly export toggle group --- .../kit-headless/src/components/toggle-group/index.tsx | 8 ++------ packages/kit-headless/src/index.ts | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/kit-headless/src/components/toggle-group/index.tsx b/packages/kit-headless/src/components/toggle-group/index.tsx index fc5b18aa7..0f2ad5e4c 100644 --- a/packages/kit-headless/src/components/toggle-group/index.tsx +++ b/packages/kit-headless/src/components/toggle-group/index.tsx @@ -1,6 +1,2 @@ -import { HToggleGroupItem } from './toggle-group-item'; -import { HToggleGroupRoot } from './toggle-group-root'; -export const ToggleGroup = { - Root: HToggleGroupRoot, - Item: HToggleGroupItem, -}; +export { HToggleGroupItem as Item } from './toggle-group-item'; +export { HToggleGroupRoot as Root } from './toggle-group-root'; diff --git a/packages/kit-headless/src/index.ts b/packages/kit-headless/src/index.ts index 77c2e051a..86198fa29 100644 --- a/packages/kit-headless/src/index.ts +++ b/packages/kit-headless/src/index.ts @@ -13,7 +13,7 @@ export * as Progress from './components/progress'; export * from './components/separator'; export * as Tabs from './components/tabs'; export { Toggle } from './components/toggle'; -export { ToggleGroup } from './components/toggle-group'; +export * as ToggleGroup from './components/toggle-group'; export * from './utils/visually-hidden'; export * as Tooltip from './components/tooltip'; export * as Dropdown from './components/dropdown'; From eaedf162a78ab90e82c8e507f8a050e923d415b2 Mon Sep 17 00:00:00 2001 From: jack shelton Date: Sun, 29 Sep 2024 07:28:34 -0500 Subject: [PATCH 3/3] add carousel --- .../docs/headless/carousel/examples/ref.tsx | 50 +++++++++++++++++++ .../routes/docs/headless/carousel/index.mdx | 6 +++ .../src/components/carousel/bullet.tsx | 4 +- .../src/components/carousel/next.tsx | 6 +++ .../src/components/carousel/previous.tsx | 5 ++ .../src/components/carousel/scroller.tsx | 5 ++ .../src/components/carousel/slide.tsx | 5 +- 7 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 apps/website/src/routes/docs/headless/carousel/examples/ref.tsx diff --git a/apps/website/src/routes/docs/headless/carousel/examples/ref.tsx b/apps/website/src/routes/docs/headless/carousel/examples/ref.tsx new file mode 100644 index 000000000..7524118af --- /dev/null +++ b/apps/website/src/routes/docs/headless/carousel/examples/ref.tsx @@ -0,0 +1,50 @@ +import { component$, useSignal, useStyles$ } from '@builder.io/qwik'; +import { Carousel } from '@qwik-ui/headless'; + +export default component$(() => { + useStyles$(styles); + + const firstSlideRef = useSignal(); + const secondSlideRef = useSignal(); + + return ( + <> + + + + + Option 1 + + + + Option 2 + + + + + + + + + ); +}); +// internal +import styles from './carousel.css?inline'; diff --git a/apps/website/src/routes/docs/headless/carousel/index.mdx b/apps/website/src/routes/docs/headless/carousel/index.mdx index a0449ce6f..d9df4ea49 100644 --- a/apps/website/src/routes/docs/headless/carousel/index.mdx +++ b/apps/website/src/routes/docs/headless/carousel/index.mdx @@ -238,6 +238,12 @@ To create a custom animation, use a CSS transition on the scroller with the `tra > Want to help make animations extend to keyframes and other goodies? Reach out to Jack in the [discord](https://discord.gg/3Vej3ehVVQ). +### Refs + +Refs can be passed to any Carousel component with the `ref` prop. + + + ## Configurations ### Pagination diff --git a/packages/kit-headless/src/components/carousel/bullet.tsx b/packages/kit-headless/src/components/carousel/bullet.tsx index 1e3296ba8..0636ac1cb 100644 --- a/packages/kit-headless/src/components/carousel/bullet.tsx +++ b/packages/kit-headless/src/components/carousel/bullet.tsx @@ -8,6 +8,7 @@ import { useSignal, sync$, useComputed$, + Signal, } from '@builder.io/qwik'; import { carouselContextId } from './context'; import { useCarousel } from './use-carousel'; @@ -18,7 +19,8 @@ type BulletProps = PropsOf<'button'> & { export const CarouselBullet = component$(({ _index, ...props }: BulletProps) => { const context = useContext(carouselContextId); - const bulletRef = useSignal(); + const internalBulletRef = useSignal(); + const bulletRef = (props.ref as Signal) ?? internalBulletRef; const slideId = `${context.localId}-${_index ?? -1}`; const isRenderedSig = useSignal(true); diff --git a/packages/kit-headless/src/components/carousel/next.tsx b/packages/kit-headless/src/components/carousel/next.tsx index ce8eb78d4..056efa700 100644 --- a/packages/kit-headless/src/components/carousel/next.tsx +++ b/packages/kit-headless/src/components/carousel/next.tsx @@ -7,12 +7,18 @@ import { useSignal, $, useComputed$, + Signal, } from '@builder.io/qwik'; import { carouselContextId } from './context'; import { useCarousel } from './use-carousel'; export const CarouselNext = component$((props: PropsOf<'button'>) => { const context = useContext(carouselContextId); + + if (props.ref) { + context.nextButtonRef = props.ref as Signal; + } + const isLastSlideInViewSig = useSignal(false); const initialLoadSig = useSignal(true); const isKeyboardFocusSig = useSignal(false); diff --git a/packages/kit-headless/src/components/carousel/previous.tsx b/packages/kit-headless/src/components/carousel/previous.tsx index 159371667..f24e98044 100644 --- a/packages/kit-headless/src/components/carousel/previous.tsx +++ b/packages/kit-headless/src/components/carousel/previous.tsx @@ -6,6 +6,7 @@ import { useSignal, $, useComputed$, + Signal, } from '@builder.io/qwik'; import { carouselContextId } from './context'; import { useCarousel } from './use-carousel'; @@ -14,6 +15,10 @@ export const CarouselPrevious = component$((props: PropsOf<'button'>) => { const context = useContext(carouselContextId); const isKeyboardFocusSig = useSignal(false); + if (props.ref) { + context.prevButtonRef = props.ref as Signal; + } + const { validIndexesSig } = useCarousel(context); const isFirstScrollableIndexSig = useComputed$(() => { diff --git a/packages/kit-headless/src/components/carousel/scroller.tsx b/packages/kit-headless/src/components/carousel/scroller.tsx index 92859ea57..d96bb3ba0 100644 --- a/packages/kit-headless/src/components/carousel/scroller.tsx +++ b/packages/kit-headless/src/components/carousel/scroller.tsx @@ -7,6 +7,7 @@ import { useOnWindow, Slot, useSignal, + Signal, } from '@builder.io/qwik'; import { carouselContextId } from './context'; import { useStyles$ } from '@builder.io/qwik'; @@ -19,6 +20,10 @@ export const CarouselScroller = component$((props: PropsOf<'div'>) => { useStyles$(styles); const context = useContext(carouselContextId); + if (props.ref) { + context.scrollerRef = props.ref as Signal; + } + const { onMouseDown$, onTouchStart$, onTouchMove$, onTouchEnd$, ...rest } = props; const isMouseMovingSig = useSignal(false); diff --git a/packages/kit-headless/src/components/carousel/slide.tsx b/packages/kit-headless/src/components/carousel/slide.tsx index 4c7c6e9ef..ad061c014 100644 --- a/packages/kit-headless/src/components/carousel/slide.tsx +++ b/packages/kit-headless/src/components/carousel/slide.tsx @@ -7,6 +7,7 @@ import { useSignal, useComputed$, $, + Signal, } from '@builder.io/qwik'; import { carouselContextId } from './context'; @@ -16,7 +17,9 @@ export type CarouselSlideProps = PropsOf<'div'> & { export const CarouselSlide = component$(({ _index, ...props }: CarouselSlideProps) => { const context = useContext(carouselContextId); - const slideRef = useSignal(); + const internalSlideRef = useSignal(); + const slideRef = (props.ref as Signal) ?? internalSlideRef; + const slideId = `${context.localId}-${_index ?? -1}`; const isVisibleSig = useComputed$(() => { const start = context.currentIndexSig.value;