Skip to content

Fix/on end reached #101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,46 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [0.0.477](https://github.com/getpingback/ui/compare/v0.0.476...v0.0.477) (2025-07-30)
### [0.0.481](https://github.com/getpingback/ui/compare/v0.0.480...v0.0.481) (2025-07-30)


### Bug Fixes

### [0.0.476](https://github.com/getpingback/ui/compare/v0.0.475...v0.0.476) (2025-07-30)
* add unique layoutId ([224a7a0](https://github.com/getpingback/ui/commits/224a7a04cea0c024085babc0969ff7f5af514ce7))

### [0.0.475](https://github.com/getpingback/ui/compare/v0.0.474...v0.0.475) (2025-07-30)
### [0.0.480](https://github.com/getpingback/ui/compare/v0.0.479...v0.0.480) (2025-07-30)


### Bug Fixes

* merge ([f0b7bae](https://github.com/getpingback/ui/commits/f0b7baeafca49835c6b31c132dc122e804c067dc))
* **variable-input:** return '\n' ([65a032f](https://github.com/getpingback/ui/commits/65a032fff1cfcb904c9feddaba21f1984fad14cb))

### [0.0.479](https://github.com/getpingback/ui/compare/v0.0.478...v0.0.479) (2025-07-30)

### [0.0.478](https://github.com/getpingback/ui/compare/v0.0.477...v0.0.478) (2025-07-30)

### [0.0.477](https://github.com/getpingback/ui/compare/v0.0.476...v0.0.477) (2025-07-30)


### Bug Fixes

* add unique key on tabs ([b9bab2e](https://github.com/getpingback/ui/commits/b9bab2e7b78084275962d756c3a369407c250698))

### [0.0.476](https://github.com/getpingback/ui/compare/v0.0.475...v0.0.476) (2025-07-29)


### Bug Fixes

* onEndReached ([f7c0410](https://github.com/getpingback/ui/commits/f7c0410d8967b6d0521c9ffe6a6c67928376c820))

### [0.0.475](https://github.com/getpingback/ui/compare/v0.0.474...v0.0.475) (2025-07-28)


### Bug Fixes

* onEndReached on combobox ([4585957](https://github.com/getpingback/ui/commits/458595769ac9cb8aa164a393cf89f64876deda97))

### [0.0.474](https://github.com/getpingback/ui/compare/v0.0.473...v0.0.474) (2025-07-11)


Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@getpingback/ui",
"author": "Pingback Team",
"version": "0.0.477",
"version": "0.0.481",
"license": "MIT",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/components/combobox/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export function Combobox({
}, 100);

return () => observer.disconnect();
}, [open, options, onEndReached]);
}, [open, onEndReached, hasSelectedStep]);

const comboboxVariants = {
default: DefaultVariant,
Expand Down
100 changes: 48 additions & 52 deletions src/components/tabs/tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,53 @@
import * as React from "react";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cva, type VariantProps } from "class-variance-authority";
import { motion } from "framer-motion";
import * as React from 'react';
import * as TabsPrimitive from '@radix-ui/react-tabs';
import { cva, type VariantProps } from 'class-variance-authority';
import { motion } from 'framer-motion';

import { cn } from "@/lib/utils";
import { cn } from '@/lib/utils';

const Tabs = TabsPrimitive.Root;

const tabTriggerVariants = cva(
"relative w-full h-full !outline-none rounded-md [&[data-state=active]]:opacity-100 font-semibold text-secondary-foreground text-xs opacity-45 hover:opacity-100 transition-all duration-200 ease-in-out",
'relative w-full h-full !outline-none rounded-md [&[data-state=active]]:opacity-100 font-semibold text-secondary-foreground text-xs opacity-45 hover:opacity-100 transition-all duration-200 ease-in-out',
{
variants: {
type: {
clear: "[&[data-state=active]]:text-[#9061F9] hover:text-[#9061F9]",
purple: "[&[data-state=active]]:text-[#7E3AF2] hover:text-[#7E3AF2]",
"bottom-line": "[&[data-state=active]]:text-[#7E3AF2] hover:text-[#7E3AF2] bg-transparent",
},
clear: '[&[data-state=active]]:text-[#9061F9] hover:text-[#9061F9]',
purple: '[&[data-state=active]]:text-[#7E3AF2] hover:text-[#7E3AF2]',
'bottom-line': '[&[data-state=active]]:text-[#7E3AF2] hover:text-[#7E3AF2] bg-transparent'
}
},
defaultVariants: {
type: "purple",
},
type: 'purple'
}
}
);

const tabListVariants = cva("inline-flex items-center justify-center rounded-md text-muted-foreground bg-[#71717A0A]", {
const tabListVariants = cva('inline-flex items-center justify-center rounded-md text-muted-foreground bg-[#71717A0A]', {
variants: {
height: {
medium: "h-[32px]",
full: "h-[40px] p-1",
},
medium: 'h-[32px]',
full: 'h-[40px] p-1'
}
},
defaultVariants: {
height: "medium",
},
height: 'medium'
}
});

export interface TabsTriggerProps
extends Omit<React.ComponentProps<typeof TabsPrimitive.Trigger>, "type">,
VariantProps<typeof tabTriggerVariants> {}
extends Omit<React.ComponentProps<typeof TabsPrimitive.Trigger>, 'type'>,
VariantProps<typeof tabTriggerVariants> {
layoutId?: string;
}

function TabsTrigger({ className, type, ...props }: TabsTriggerProps) {
function TabsTrigger({ className, type, layoutId, ...props }: TabsTriggerProps) {
const [isActive, setIsActive] = React.useState(false);
const triggerRef: React.MutableRefObject<HTMLButtonElement | null> = React.useRef(null);

React.useEffect(() => {
const handleStateChange = () => {
if (triggerRef.current && triggerRef.current.getAttribute("data-state") === "active") {
if (triggerRef.current && triggerRef.current.getAttribute('data-state') === 'active') {
setIsActive(true);
} else {
setIsActive(false);
Expand All @@ -56,7 +58,7 @@ function TabsTrigger({ className, type, ...props }: TabsTriggerProps) {

const observer = new MutationObserver(handleStateChange);
if (triggerRef.current) {
observer.observe(triggerRef.current, { attributes: true, attributeFilter: ["data-state"] });
observer.observe(triggerRef.current, { attributes: true, attributeFilter: ['data-state'] });
}

return () => {
Expand All @@ -68,27 +70,26 @@ function TabsTrigger({ className, type, ...props }: TabsTriggerProps) {
<>
<TabsPrimitive.Trigger
ref={triggerRef}
className={cn(tabTriggerVariants({ type }), "relative", className)}
className={cn(tabTriggerVariants({ type }), 'relative', className)}
style={{
WebkitTapHighlightColor: "transparent",
WebkitTapHighlightColor: 'transparent'
}}
data-testid='tabs-trigger'
data-testid="tabs-trigger"
{...props}
>
<div className='relative z-30'>{props.children}</div>
<div className="relative z-30">{props.children}</div>

{isActive ? (
<motion.div
key={props.value}
className={cn(
"absolute rounded-md z-20 debug-border",
type === "clear" &&
"inset-0 z-[-1] bg-[#FFFFFF] [box-shadow:0px_0px_1px_1px_rgba(0,_0,_0,_0.04)] opacity-100",
type === "purple" && "inset-0 bg-[#9061F914] outline-none",
type === "bottom-line" && "bg-[#9061F9] h-[2px] left-0 right-0 bottom-0 rounded-none"
'absolute rounded-md z-20 debug-border',
type === 'clear' && 'inset-0 z-[-1] bg-[#FFFFFF] [box-shadow:0px_0px_1px_1px_rgba(0,_0,_0,_0.04)] opacity-100',
type === 'purple' && 'inset-0 bg-[#9061F914] outline-none',
type === 'bottom-line' && 'bg-[#9061F9] h-[2px] left-0 right-0 bottom-0 rounded-none'
)}
layoutId={`${type}-tab-active-indicator`}
transition={{ type: "spring", bounce: 0, duration: 0.6 }}
layoutId={layoutId ? `${layoutId}-${type}-tab-active-indicator` : `${type}-tab-active-indicator`}
transition={{ type: 'spring', bounce: 0, duration: 0.6 }}
/>
) : null}
</TabsPrimitive.Trigger>
Expand All @@ -99,36 +100,31 @@ function TabsTrigger({ className, type, ...props }: TabsTriggerProps) {
export interface TabWrapperProps {
type: string;
children: React.ReactNode;
layoutId?: string;
}

function TabWrapper({ type, children }: TabWrapperProps) {
function TabWrapper({ type, children, layoutId }: TabWrapperProps) {
if (React.Children.count(children) !== 1) return;

const child = React.Children.only(children) as React.ReactElement<any>;
return React.cloneElement(child, { type });
return React.cloneElement(child, { type, layoutId });
}

export interface TabProps
extends Omit<React.ComponentProps<typeof TabsPrimitive.List>, "type">,
VariantProps<typeof tabTriggerVariants> {
type: "purple" | "clear" | "bottom-line";
height: "medium" | "full";
export interface TabProps extends Omit<React.ComponentProps<typeof TabsPrimitive.List>, 'type'>, VariantProps<typeof tabTriggerVariants> {
type: 'purple' | 'clear' | 'bottom-line';
height: 'medium' | 'full';
layoutId?: string;
}

function TabsList({ className, type = "purple", height, ...props }: TabProps) {
function TabsList({ className, type = 'purple', height, layoutId, ...props }: TabProps) {
return (
<TabsPrimitive.List
className={cn(
tabListVariants({ height }),
"relative z-[9] outline-none",
type === "bottom-line" && "bg-transparent",
className
)}
data-testid='tabs-list'
className={cn(tabListVariants({ height }), 'relative z-[9] outline-none', type === 'bottom-line' && 'bg-transparent', className)}
data-testid="tabs-list"
{...props}
>
{React.Children.map(props.children, (child, index) => (
<TabWrapper key={index} type={type}>
<TabWrapper key={index} type={type} layoutId={layoutId}>
{child}
</TabWrapper>
))}
Expand All @@ -141,8 +137,8 @@ export interface TabContentProps extends React.ComponentProps<typeof TabsPrimiti
function TabsContent({ className, ...props }: TabContentProps) {
return (
<TabsPrimitive.Content
className={cn("mt-2 !outline-none focus-visible:!outline-none focus:!outline-none", className)}
data-testid='tabs-content'
className={cn('mt-2 !outline-none focus-visible:!outline-none focus:!outline-none', className)}
data-testid="tabs-content"
{...props}
/>
);
Expand Down