Skip to content

Commit

Permalink
Merge pull request #75 from kunai-consulting/scroll-area
Browse files Browse the repository at this point in the history
Work on the scroll-area component
  • Loading branch information
thejackshelton-kunaico authored Dec 23, 2024
2 parents 0241c0f + 4f42e3e commit 386670e
Show file tree
Hide file tree
Showing 21 changed files with 824 additions and 6 deletions.
6 changes: 6 additions & 0 deletions .changeset/new-coats-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kunai-consulting/qwik-components": patch
"qwik-design-system-docs": patch
---

Add the scroll-area component
1 change: 1 addition & 0 deletions apps/docs/src/routes/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Here is some MDX
- [Checkbox](checkbox)
- [Pagination](pagination)
- [OTP](otp)
- [Scroll Area](scroll-area)
1 change: 1 addition & 0 deletions apps/docs/src/routes/menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Checkbox](/checkbox)
- [Pagination](/pagination)
- [OTP](/otp)
- [Scroll Area](/scroll-area)

## Styled

Expand Down
33 changes: 33 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/both.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { component$, useStyles$ } from "@builder.io/qwik";
import { ScrollArea } from "@kunai-consulting/qwik-components";
import styles from './scroll-area.css?inline';

export default component$(() => {
useStyles$(styles);
return (
<ScrollArea.Root class="!h-[200px] !w-[250px] rounded-md border p-4 scroll-area-root">
<ScrollArea.ViewPort class="scroll-area-viewport">
<div class="p-4">
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos
impedit rem, repellat deserunt ducimus quasi nisi voluptatem cumque
aliquid esse ea deleniti eveniet incidunt! Deserunt minus laborum
accusamus iusto dolorum. Lorem ipsum dolor sit, amet consectetur
adipisicing elit. Blanditiisofficiiserrorminimaeos fugit voluptate
excepturi eveniet dolore et, ratione impedit consequuntur dolorem hic
quae corrupti autem? Dolorem, sit voluptatum.
</p>
</div>
</ScrollArea.ViewPort>

<ScrollArea.Scrollbar orientation="vertical" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb"/>
</ScrollArea.Scrollbar>

<ScrollArea.Scrollbar orientation="horizontal" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb"/>
</ScrollArea.Scrollbar>
</ScrollArea.Root>
);
});

29 changes: 29 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/horizontal-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { component$, useStyles$ } from "@builder.io/qwik";
import { ScrollArea } from "@kunai-consulting/qwik-components";
import styles from './scroll-area.css?inline';

export default component$(() => {
useStyles$(styles);
return (
<ScrollArea.Root class="scroll-area-root" style={{height: "200px", width: "100px"}}>
<ScrollArea.ViewPort class="scroll-area-viewport">
<div>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos
impedit rem, repellat deserunt ducimus quasi nisi voluptatem cumque
aliquid esse ea deleniti eveniet incidunt! Deserunt minus laborum
accusamus iusto dolorum. Lorem ipsum dolor sit, amet consectetur
adipisicing elit. Blanditiisofficiiserror minima eos fugit voluptate
excepturi eveniet dolore et, ratione impedit consequuntur dolorem hic
quae corrupti autem? Dolorem, sit voluptatum.
</p>
</div>
</ScrollArea.ViewPort>

<ScrollArea.Scrollbar orientation="horizontal" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb"/>
</ScrollArea.Scrollbar>
</ScrollArea.Root>
);
});

29 changes: 29 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/horizontal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { component$, useStyles$ } from "@builder.io/qwik";
import { ScrollArea } from "@kunai-consulting/qwik-components";
import styles from './scroll-area.css?inline';

export default component$(() => {
useStyles$(styles);
return (
<ScrollArea.Root class="!h-[200px] !w-[100px] rounded-md border p-4 scroll-area-root">
<ScrollArea.ViewPort class="scroll-area-viewport">
<div class="p-4">
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos
impedit rem, repellat deserunt ducimus quasi nisi voluptatem cumque
aliquid esse ea deleniti eveniet incidunt! Deserunt minus laborum
accusamus iusto dolorum. Lorem ipsum dolor sit, amet consectetur
adipisicing elit. Blanditiis officiis error minima eos fugit voluptate
excepturi eveniet dolore et, ratione impedit consequuntur dolorem hic
quae corrupti autem? Dolorem, sit voluptatum.
</p>
</div>
</ScrollArea.ViewPort>

<ScrollArea.Scrollbar orientation="horizontal" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb"/>
</ScrollArea.Scrollbar>
</ScrollArea.Root>
);
});

64 changes: 64 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/scroll-area.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.scroll-area-root {
background: #0a4d70;
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
--thumb-size: 12px;
}

.scroll-area-viewport {
width: 100%;
height: 100%;
border-radius: inherit;
overflow: scroll;
position: relative;
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */
}

/* Hide default scrollbars */
.scroll-area-viewport::-webkit-scrollbar {
display: none;
}

.scroll-area-scrollbar {
display: flex;
touch-action: none;
user-select: none;
padding: 2px;
background: rgba(0, 0, 0, 0.05);
transition: background 160ms ease-out;
position: absolute;
}

.scroll-area-scrollbar[data-orientation='vertical'] {
width: 16px;
height: 100%;
right: 0;
top: 0;
}

.scroll-area-scrollbar[data-orientation='horizontal'] {
height: 16px;
width: calc(100% - var(--thumb-size));
bottom: 0;
left: 0;
}

.scroll-area-thumb {
position: relative;
height: var(--thumb-size);
width: var(--thumb-size);
background: rgba(0, 0, 0, 0.3);
border-radius: 9999px;
transition: background 160ms ease-out;
}

.scroll-area-thumb:hover {
background: rgba(0, 0, 0, 0.5);
}

.scroll-area-thumb[data-dragging] {
background: rgba(0, 0, 0, 0.7);
}
28 changes: 28 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/vertical-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { component$, useStyles$ } from "@builder.io/qwik";
import { ScrollArea } from "@kunai-consulting/qwik-components";
import styles from "./scroll-area.css?inline";

export default component$(() => {
useStyles$(styles);
return (
<ScrollArea.Root class="scroll-area-root" style={{ height: "150px", width: "250px" }}>
<ScrollArea.ViewPort class="scroll-area-viewport">
{/* Content with fixed height to ensure scrolling */}
<div>
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos impedit
rem, repellat deserunt ducimus quasi nisi voluptatem cumque aliquid esse ea
deleniti eveniet incidunt! Deserunt minus laborum accusamus iusto dolorum.
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Blanditiis officiis
error minima eos fugit voluptate excepturi eveniet dolore et, ratione impedit
consequuntur dolorem hic quae corrupti autem? Dolorem, sit voluptatum.
</p>
</div>
</ScrollArea.ViewPort>

<ScrollArea.Scrollbar orientation="vertical" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb" />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
);
});
30 changes: 30 additions & 0 deletions apps/docs/src/routes/scroll-area/examples/vertical.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { component$, useStyles$ } from "@builder.io/qwik";
import { ScrollArea } from "@kunai-consulting/qwik-components";
import styles from './scroll-area.css?inline';

export default component$(() => {
useStyles$(styles);
return (
<ScrollArea.Root class="!h-[200px] !w-[350px] rounded-md border p-4 scroll-area-root">
<ScrollArea.ViewPort class="scroll-area-viewport">
<div class="p-4">
<p>
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Dignissimos
impedit rem, repellat deserunt ducimus quasi nisi voluptatem cumque
aliquid esse ea deleniti eveniet incidunt! Deserunt minus laborum
accusamus iusto dolorum. Lorem ipsum dolor sit, amet consectetur
adipisicing elit. Blanditiis officiis error minima eos fugit voluptate
excepturi eveniet dolore et, ratione impedit consequuntur dolorem hic
quae corrupti autem? Dolorem, sit voluptatum.
</p>
</div>
</ScrollArea.ViewPort>

<ScrollArea.Scrollbar orientation="vertical" class="scroll-area-scrollbar">
<ScrollArea.Thumb class="scroll-area-thumb"/>
</ScrollArea.Scrollbar>

</ScrollArea.Root>
);
});

60 changes: 60 additions & 0 deletions apps/docs/src/routes/scroll-area/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Qwik Design System | Scroll Area
---

# Scroll Area

A scrollable container that provides a custom-styled scrollbar with support for both vertical and horizontal scrolling.

<Showcase name="vertical" />

## Basic Usage

The Scroll Area component provides a customizable scrolling container that replaces the browser's default scrollbars with a more aesthetically pleasing design.

## Anatomy

The Scroll Area component is composed of several parts:

```tsx
<ScrollArea.Root>
<ScrollArea.ViewPort>
{/* Your content */}
</ScrollArea.ViewPort>
<ScrollArea.Scrollbar orientation="vertical">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
<ScrollArea.Scrollbar orientation="horizontal">
<ScrollArea.Thumb />
</ScrollArea.Scrollbar>
</ScrollArea.Root>
```

## Vertical Scrolling
By default, Scroll Area supports vertical scrolling when content exceeds the container height.

<Showcase name="vertical" />

## Horizontal Scrolling
To enable horizontal scrolling, add the horizontal scrollbar component.
<Showcase name="horizontal" />

## Both Directions
Scroll Area supports both vertical and horizontal scrolling simultaneously.
<Showcase name="both" />

## Custom Styling
The Scroll Area component can be styled using CSS classes.

## Sizing
The Scroll Area component adapts to its container size. You can control its dimensions using standard CSS properties.

## Hidden Scrollbars
The Scroll Area component automatically hides the native browser scrollbars while maintaining scroll functionality.

## Scroll Thumb Behavior
The scroll thumb responds to:

* Direct dragging
* Click and drag interactions
* Content scrolling
12 changes: 6 additions & 6 deletions libs/components/src/checklist/checklist-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ import {
useSignal,
useTask$,
useVisibleTask$,
$,
} from '@builder.io/qwik';
import { CheckboxRoot } from '../checkbox/checkbox-root';
import { ChecklistContext, type ChecklistState } from './checklist-context';
$
} from "@builder.io/qwik";
import { CheckboxRoot } from "../checkbox/checkbox-root";
import { ChecklistContext, type ChecklistState } from "./checklist-context";

interface ChecklistItemProps extends PropsOf<'div'> {
interface ChecklistItemProps extends PropsOf<"div"> {
_index?: number;
}

export const ChecklistItem = component$((props: ChecklistItemProps) => {
const { _index, ...rest } = props;

if (_index === undefined) {
throw new Error('Checklist Item must have an index.');
throw new Error("Checklist Item must have an index.");
}

const context = useContext(ChecklistContext);
Expand Down
1 change: 1 addition & 0 deletions libs/components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * as Otp from './otp';
export * as Checkbox from './checkbox';
export * as Checklist from './checklist';
export * as Pagination from "./pagination";
export * as ScrollArea from './scroll-area';
4 changes: 4 additions & 0 deletions libs/components/src/scroll-area/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { ScrollAreaRoot as Root } from './scroll-area-root';
export { ScrollAreaViewPort as ViewPort } from './scroll-area-view-port';
export { ScrollAreaScrollbar as Scrollbar } from './scroll-area-scrollbar';
export { ScrollAreaThumb as Thumb } from './scroll-area-thumb';
18 changes: 18 additions & 0 deletions libs/components/src/scroll-area/research.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Headless ScrollArea Component

## API

```
<ScrollArea.Root>
<ScrollArea.ViewPort>
{/* data */}
<ScrollArea.ViewPort/>
<ScrollArea.ScrollBar> {/* horizontal */}
<ScrollArea.Thumb />
<ScrollArea.ScrollBar/>
<ScrollArea.ScrollBar> {/* vertical */}
<ScrollArea.Thumb />
<ScrollArea.ScrollBar/>
</ScrollArea.Root>
```
10 changes: 10 additions & 0 deletions libs/components/src/scroll-area/scroll-area-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createContextId, type Signal } from "@builder.io/qwik";

export const scrollAreaContextId =
createContextId<ScrollAreaContext>("scroll-area-context");

export interface ScrollAreaContext {
scrollbarRef: Signal<HTMLDivElement | undefined>;
thumbRef: Signal<HTMLDivElement | undefined>;
viewportRef: Signal<HTMLDivElement | undefined>;
}
30 changes: 30 additions & 0 deletions libs/components/src/scroll-area/scroll-area-root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
component$,
type PropsOf,
Slot,
useContextProvider,
useSignal
} from "@builder.io/qwik";
import { scrollAreaContextId } from "./scroll-area-context";

type RootProps = PropsOf<"div">;

export const ScrollAreaRoot = component$<RootProps>((props) => {
const viewportRef = useSignal<HTMLDivElement>();
const scrollbarRef = useSignal<HTMLDivElement>();
const thumbRef = useSignal<HTMLDivElement>();

const context = {
viewportRef,
scrollbarRef,
thumbRef
};

useContextProvider(scrollAreaContextId, context);

return (
<div {...props} data-scroll-area-root>
<Slot />
</div>
);
});
Loading

0 comments on commit 386670e

Please sign in to comment.