Skip to content

Commit

Permalink
Merge pull request #5 from lovrozagar/utils-init
Browse files Browse the repository at this point in the history
Utils init
  • Loading branch information
lovrozagar authored Oct 19, 2024
2 parents 138774b + d518301 commit eab538f
Show file tree
Hide file tree
Showing 36 changed files with 616 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/components/link/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const DEFAULT_LINK_CLASSES =
'_link tap-highlight-transparent appearence-none m-0 box-border inline-flex cursor-pointer items-center bg-transparent p-0 text-base text-[rgba(var(--link-color))] underline-offset-2 outline-none ring-ring-color ring-offset-background transition-[color,box-shadow] duration-fast focus-visible:ring-[2px] focus-visible:ring-offset-offset active:text-[rgba(var(--link-color),0.8)]'

const LINK_UNDERLINE_CLASSES = {
none: undefined,
hover: 'hover:underline',
active: 'active:underline',
focus: 'focus:underline',
'focus-visible': 'focus-visible:underline',
}

export { DEFAULT_LINK_CLASSES, LINK_UNDERLINE_CLASSES }

21 changes: 21 additions & 0 deletions src/components/type-writer/components/type-writter-cursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useTypeWritterContext } from '@/components/type-writer/contexts/type-writter-context'
import type { TypeWritterCursorProps } from '@/components/type-writer/types/type-writter-cursor'
import { cn, polymorphic } from '@renderui/utils'

const TypeWritterCursor = (props: TypeWritterCursorProps) => {
const { asChild, className, children = '|', ...restProps } = props

const { cursorRef } = useTypeWritterContext()

const Component = polymorphic(asChild, 'span')

return (
<Component
ref={cursorRef}
className={cn('_type-writter-cursor font-extralight', className)}
{...restProps}
/>
)
}

export { TypeWritterCursor }
65 changes: 65 additions & 0 deletions src/components/type-writer/components/type-writter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { AsChildProp } from '@/components/_shared/types/as-child'
import { TypeWritterProvider } from '@/components/type-writer/contexts/type-writter-context'
import { cn, getNestedChildrenTextContent, polymorphic } from '@renderui/utils'
import { useEffect, useRef, useState } from 'react'

type TypeWriterProps = React.ComponentPropsWithRef<'span'> &
AsChildProp & {
cursorProps: React.ComponentPropsWithRef<'span'> & AsChildProp
}

const TypeWriter = (props: TypeWriterProps) => {
const { asChild, className, children, cursorProps } = props

const { asChild: cursorAsChild, className: cursorClassName, ...restCursorProps } = cursorProps

const [currentText, setCurrentText] = useState('')
const cursorRef = useRef<HTMLSpanElement>(null)

/* Blink the cursor only when the animation is not playing */
useEffect(() => {
const nodeTextContent = getNestedChildrenTextContent(children)
const typing = currentText !== nodeTextContent

if (cursorRef.current) {
cursorRef.current.style.animation = typing ? 'none' : 'blink 2s infinite'
}
}, [currentText, children])

useEffect(() => {
/* If the text is different, start the animation */
const interval = setInterval(
() => {
const nodeTextContent = getNestedChildrenTextContent(children)

/* If the text is the same, stop the animation (should never be reached) */
if (nodeTextContent === currentText) {
clearInterval(interval)
return
}

/* if currentText is included in toText, add one more character */
if (nodeTextContent.startsWith(currentText)) {
setCurrentText(nodeTextContent.slice(0, currentText.length + 1))
} else {
/* if currentText is not included in toText, remove one character */
setCurrentText(currentText.slice(0, currentText.length - 1))
}
},
Math.random() * 6 * 10,
)
return () => {
clearInterval(interval)
}
}, [children, currentText])

const Component = polymorphic(asChild, 'span')

return (
<Component className={cn('text-base', className)}>
<TypeWritterProvider value={{ cursorRef }}>{currentText}</TypeWritterProvider>
</Component>
)
}

export { TypeWriter }
12 changes: 12 additions & 0 deletions src/components/type-writer/contexts/type-writter-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { initializeContext } from '@renderui/utils'

import type { TypeWriterContext } from '@/components/type-writer/types/type-writter-context'

const [TypeWritterProvider, useTypeWritterContext] = initializeContext<TypeWriterContext>({
errorMessage: 'Components using typeWritter context must be wrapped in a <TypeWritter />.',
providerName: 'TypeWritterProvider',
hookName: 'useTypeWritterContext',
name: 'TypeWritterContext',
})

export { TypeWritterProvider, useTypeWritterContext }
7 changes: 7 additions & 0 deletions src/components/type-writer/types/type-writter-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { RefObject } from 'react'

type TypeWriterContext = {
cursorRef: RefObject<HTMLSpanElement>
}

export type { TypeWriterContext }
12 changes: 12 additions & 0 deletions src/components/type-writer/types/type-writter-cursor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { AsChildProp } from '@/components/_shared/types/as-child'
import type { Simplify } from '@/components/_shared/types/simplify'

type TypeWritterCursorPrimitiveProps = React.ComponentPropsWithRef<'span'>

type TypeWritterCursorCustomProps = AsChildProp

type TypeWritterCursorProps = Simplify<
TypeWritterCursorPrimitiveProps & TypeWritterCursorCustomProps
>

export type { TypeWritterCursorProps }
10 changes: 10 additions & 0 deletions src/components/type-writer/types/type-writter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { AsChildProp } from '@/components/_shared/types/as-child'
import type { Simplify } from '@/components/_shared/types/simplify'

type TypeWritterPrimitiveProps = React.ComponentPropsWithRef<'span'>

type TypeWritterCustomProps = AsChildProp

type TypeWritterProps = Simplify<TypeWritterPrimitiveProps & TypeWritterCustomProps>

export type { TypeWritterProps }
7 changes: 7 additions & 0 deletions src/packages/components/accordion/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# @renderui/accordion

## 0.0.1

### Patch changes

- Added accordion component
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { act, render } from '@testing-library/react'
import userEvent, { UserEvent } from '@testing-library/user-event'
33 changes: 33 additions & 0 deletions src/packages/components/accordion/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"author": {
"email": "[email protected]",
"name": "Lovro Žagar"
},
"dependencies": {
"@radix-ui/react-accordion": "^1.2.1"
},
"devDependencies": {
"bunchee": "^5.5.1",
"react": "19.0.0-rc-a960b92c-20240819",
"react-dom": "19.0.0-rc-a960b92c-20240819",
"types-react": "^19.0.0-rc.1",
"types-react-dom": "^19.0.0-rc.1",
"typescript": "^5.5.4"
},
"license": "MIT",
"name": "@renderui/accordion",
"peerDependencies": {
"@renderui/utils": ">=0.2.0",
"react": ">=18",
"react-dom": ">=18"
},
"private": false,
"repository": {
"type": "git",
"url": "https://github.com/lovrozagar/renderui"
},
"scripts": {
"build": "bunchee -m"
},
"version": "0.0.1"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client'

import { AccordionContent as AccordionContentPrimitive } from '@radix-ui/react-accordion'
import {
DEFAULT_ACCORDION_CONTENT_CHILDREN_CONTAINER_CLASSNAME,
DEFAULT_ACCORDION_CONTENT_CLASSNAME,
} from '../constants/constants'
import type { AccordionContentProps } from '../types/accordion-content'

const AccordionContent = (props: AccordionContentProps) => {
const {
className,
style,
children,
childrenContainerProps,
animationDuration,
animationInDuration,
animationOutDuration,
animationTimingFunction,
animationInTimingFunction,
animationOutTimingFunction,
...restProps
} = props

const {
asChild,
className: childrenContainerClassName,
...restChildrenContainerProps
} = getOptionalObject(childrenContainerProps)

const AccordionContentChildrenContainer = polymorphic(asChild, 'div')

return (
<AccordionContentPrimitive
data-slot='content'
className={cn(DEFAULT_ACCORDION_CONTENT_CLASSNAME, className)}
style={{
...getAnimationStyleVariables({
animationDuration,
animationInDuration,
animationOutDuration,
animationTimingFunction,
animationInTimingFunction,
animationOutTimingFunction,
defaultAnimationDuration: 200,
defaultAnimationTimingFunction: 'ease-out',
}),
...style,
}}
{...restProps}
>
<AccordionContentChildrenContainer
data-slot='content-children-container'
className={cn(
DEFAULT_ACCORDION_CONTENT_CHILDREN_CONTAINER_CLASSNAME,
childrenContainerClassName,
)}
{...restChildrenContainerProps}
>
{children}
</AccordionContentChildrenContainer>
</AccordionContentPrimitive>
)
}

AccordionContent.displayName = 'AccordionContent'

export { AccordionContent }
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use client'

import { AccordionItem as AccordionItemPrimitive } from '@radix-ui/react-accordion'
import { cn } from '@renderui/utils'
import { DEFAULT_ACCORDION_ITEM_CLASSNAME } from '../constants/constants'
import type { AccordionItemProps } from '../types/accordion-item'

const AccordionItem = (props: AccordionItemProps) => {
const { className, ...restProps } = props

return (
<AccordionItemPrimitive
data-slot='item'
className={cn(DEFAULT_ACCORDION_ITEM_CLASSNAME, className)}
{...restProps}
/>
)
}

export { AccordionItem }
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { AccordionTrigger } from '@radix-ui/react-accordion'
import type { AccordionTriggerPrimitiveProps } from '../types/accordion-trigger-primitive'

const AccordionTriggerPrimitive = (props: AccordionTriggerPrimitiveProps) => {
return <AccordionTrigger {...props} />
}

export { AccordionTriggerPrimitive }
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use client'

import {
AccordionHeader as AccordionHeaderPrimitive,
AccordionTrigger as AccordionTriggerPrimitive,
} from '@radix-ui/react-accordion'
import type { AccordionTriggerProps } from '../types/accordion-trigger'
import {
DEFAULT_ACCORDION_HEADER_CLASSNAME,
DEFAULT_ACCORDION_TRIGGER_CLASSNAME,
DEFAULT_ACCORDION_TRIGGER_ICON_CLASSNAME,
} from '../constants/constants'

const AccordionTrigger = (props: AccordionTriggerProps) => {
const {
ref,
className,
children,
icon,
iconProps,
accordionHeaderProps,
hasIcon = true,
hasRipple = false,
hasDefaultPressedStyles = false,
...restProps
} = props

const { className: headerClassName, ...restAccordionHeaderProps } =
getOptionalObject(accordionHeaderProps)

const { className: iconClassName, ...restIconProps } = getOptionalObject(iconProps)

const renderIcon = () => {
if (!hasIcon) return null

if (icon) return icon

return (
<ChevronDownIcon
className={cn(DEFAULT_ACCORDION_TRIGGER_ICON_CLASSNAME, iconClassName)}
{...restIconProps}
/>
)
}

return (
<AccordionHeaderPrimitive
data-slot='header'
className={cn(DEFAULT_ACCORDION_HEADER_CLASSNAME, headerClassName)}
{...restAccordionHeaderProps}
>
<AccordionTriggerPrimitive asChild>
<Button
ref={ref}
variant='plain'
data-slot='trigger'
hasRipple={hasRipple}
hasDefaultPressedStyles={hasDefaultPressedStyles}
className={cn(DEFAULT_ACCORDION_TRIGGER_CLASSNAME, className)}
{...restProps}
>
{children}
{renderIcon()}
</Button>
</AccordionTriggerPrimitive>
</AccordionHeaderPrimitive>
)
}

export { AccordionTrigger }
19 changes: 19 additions & 0 deletions src/packages/components/accordion/src/components/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use client'

import { Accordion as AccordionPrimitive } from '@radix-ui/react-accordion'
import { DEFAULT_ACCORDION_CLASSNAME } from '../constants/constants'
import type { AccordionProps } from '../types/accordion'

const Accordion = (props: AccordionProps) => {
const { className, ...restProps } = props

return (
<AccordionPrimitive
data-slot='base'
className={cn(DEFAULT_ACCORDION_CLASSNAME, className)}
{...restProps}
/>
)
}

export { Accordion }
Loading

0 comments on commit eab538f

Please sign in to comment.