Skip to content
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

feat(core): useComposeRefs and useForwardRef #252

Merged
merged 7 commits into from
Jul 31, 2023
Merged
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
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ node_modules
coverage
dist
storybook-static
.nuxt
.nuxt
*.md
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ Thanks to Daniel Roe [@danielroe](https://github.com/danielroe). Nuxt has helped

Thanks to Kevin Deng [@sxzz](https://github.com/sxzz). Helped me a lot with the issues I was stuck in Vue. and [Vue Macros](https://vue-macros.sxzz.moe) is a great project.

## Credits

- [Skirtle](https://github.com/skirtles-code)


---

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"dev": "pnpm storybook dev -p 6006 --no-open",
"dev:all": "turbo run dev --filter='./packages/**' --concurrency $(($(ls -1 packages/components packages/core | wc -l)+3))",
"dev:core": "turbo run dev --filter='./packages/core/**'",
"dev:components": "turbo run dev --filter='./packages/components/**'",
"lint": "eslint --cache \"*.{[jt]s?(x),vue}\"",
"lint:fix": "eslint --fix --cache \"*.{[jt]s?(x),vue}\"",
"dev:components": "turbo run dev --filter='./packages/components/**' --concurrency $(($(ls -1 packages/components | wc -l)+3))",
"lint": "eslint . --cache ",
"lint:fix": "eslint . --fix --cache",
"play:vue": "pnpm clean:dts && turbo run dev --filter='./playground/vue3/**'",
"play:nuxt": "pnpm clean:dts && turbo run dev --filter='./playground/nuxt3/**'",
"play": "pnpm clean:dts && turbo run dev --filter='./playground/**'",
Expand Down Expand Up @@ -48,10 +48,10 @@
"@oku-ui/provide": "workspace:^",
"@oku-ui/separator": "workspace:^",
"@oku-ui/switch": "workspace:^",
"@oku-ui/visually-hidden": "workspace:^",
"@oku-ui/toggle": "workspace:^",
"@oku-ui/use-composable": "workspace:^",
"@oku-ui/utils": "workspace:^",
"@oku-ui/visually-hidden": "workspace:^",
"@storybook/addon-essentials": "^7.0.27",
"@storybook/addon-interactions": "^7.0.27",
"@storybook/addon-links": "^7.0.27",
Expand Down
24 changes: 10 additions & 14 deletions packages/components/arrow/src/arrow.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { cloneVNode, defineComponent, h } from 'vue'
import type { ElementType, MergeProps, PrimitiveProps, RefElement } from '@oku-ui/primitive'
import type { ElementType, InstanceTypeRef, MergeProps, PrimitiveProps } from '@oku-ui/primitive'
import { Primitive } from '@oku-ui/primitive'
import { useRef } from '@oku-ui/use-composable'
import { useForwardRef } from '@oku-ui/use-composable'

type ArrowElement = ElementType<'svg'>
export type _ArrowEl = SVGSVGElement

interface ArrowProps extends PrimitiveProps {}

const NAME = 'Arrow'
Expand All @@ -17,15 +19,11 @@ const arrow = defineComponent({
default: false,
},
},
setup(props, { attrs, slots, expose }) {
const { $el, newRef } = useRef()
setup(props, { attrs, slots }) {
const forwardedRef = useForwardRef()

const { width = '10px', height = '5px', ...arrowAttrs } = attrs as ArrowElement

expose({
innerRef: $el,
})

const originalReturn = () => {
const defaultSlot = typeof slots.default === 'function' ? slots.default()[0] : slots.default ?? null
return props.asChild
Expand All @@ -40,7 +38,7 @@ const arrow = defineComponent({
: null
: h(Primitive.svg, {
...arrowAttrs,
ref: newRef,
ref: forwardedRef,
width,
height,
viewBox: '0 0 30 10',
Expand All @@ -53,17 +51,15 @@ const arrow = defineComponent({
})
}

return originalReturn as unknown as {
innerRef: ArrowElement
}
return originalReturn
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
type _ArrowProps = MergeProps<ArrowProps, ArrowElement>
type ArrowRef = RefElement<typeof arrow>
type InstanceArrowType = InstanceTypeRef<typeof arrow, _ArrowEl>

const OkuArrow = arrow as typeof arrow & (new () => { $props: _ArrowProps })

export { OkuArrow }
export type { ArrowProps, ArrowElement, ArrowRef }
export type { ArrowProps, ArrowElement, InstanceArrowType }
2 changes: 1 addition & 1 deletion packages/components/arrow/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {
OkuArrow,
} from './arrow'
export type { ArrowProps, ArrowElement, ArrowRef } from './arrow'
export type { ArrowProps, ArrowElement, InstanceArrowType, _ArrowEl } from './arrow'
6 changes: 3 additions & 3 deletions packages/components/arrow/src/stories/ArrowDemo.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { ArrowRef } from '@oku-ui/arrow'
import type { InstanceArrowType } from '@oku-ui/arrow'
import { OkuArrow } from '@oku-ui/arrow'

export interface OkuArrowProps {
Expand All @@ -12,12 +12,12 @@
template: '#1',
})

const arrowRef = ref<ArrowRef>()
const arrowRef = ref<InstanceArrowType>()
onMounted(() => {
// eslint-disable-next-line no-console
console.log(arrowRef.value?.innerRef)
console.log(arrowRef.value?.$el)
})
const alert = () => window.alert('clicked')

Check warning on line 20 in packages/components/arrow/src/stories/ArrowDemo.vue

View workflow job for this annotation

GitHub Actions / autofix

'alert' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 20 in packages/components/arrow/src/stories/ArrowDemo.vue

View workflow job for this annotation

GitHub Actions / autofix

Unexpected alert
</script>

<template>
Expand Down
3 changes: 2 additions & 1 deletion packages/components/aspect-ratio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"vue": "^3.3.0"
},
"dependencies": {
"@oku-ui/primitive": "latest"
"@oku-ui/primitive": "latest",
"@oku-ui/use-composable": "latest"
},
"devDependencies": {
"tsconfig": "workspace:^"
Expand Down
43 changes: 6 additions & 37 deletions packages/components/aspect-ratio/src/aspect-ratio.test.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import { axe } from 'vitest-axe'
import { OkuAspectRatio } from './aspect-ratio'

describe('OkuAspectRatio', () => {
it('renders the component correctly', () => {
const wrapper = mount(OkuAspectRatio)

expect(wrapper.exists()).toBe(true)
expect(wrapper.find('[data-radix-aspect-ratio-wrapper]').exists()).toBe(
true,
)
})

it('calculates the aspect ratio correctly', () => {
const ratio = 16 / 9
const wrapper = mount(OkuAspectRatio, {
props: {
ratio,
},
})

const wrapperElement = wrapper.find('[data-radix-aspect-ratio-wrapper]')

expect(wrapperElement.attributes('style')).toContain(
`padding-bottom: ${100 / ratio}%`,
)
})

it('updates aspect ratio when the prop changes', async () => {
const wrapper = mount(OkuAspectRatio, {
props: {
ratio: 4 / 3,
},
})

await wrapper.setProps({ ratio: 3 / 2 })

const wrapperElement = wrapper.find('[data-radix-aspect-ratio-wrapper]')
const computedStyle = wrapperElement.attributes('style')

if (!computedStyle)
throw new Error('No style attribute found')

const actualRatio = Number.parseFloat(computedStyle.match(/padding-bottom: (.*)%/)?.[1] ?? '0')
const expectedRatio = 66.6667
it('should have no accessibility violations', async () => {
const _wrapper = mount(OkuAspectRatio)

expect(actualRatio).toBeCloseTo(expectedRatio, 4)
const results = await axe(_wrapper.element)
// @ts-expect-error toHaveNoViolations add types project
expect(results).toHaveNoViolations()
})
})
26 changes: 11 additions & 15 deletions packages/components/aspect-ratio/src/aspect-ratio.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { ComponentPublicInstance } from 'vue'
import { computed, defineComponent, h, ref } from 'vue'
import type { ElementType, MergeProps, PrimitiveProps, RefElement } from '@oku-ui/primitive'
import { defineComponent, h } from 'vue'
import type { ElementType, InstanceTypeRef, MergeProps, PrimitiveProps } from '@oku-ui/primitive'
import { Primitive } from '@oku-ui/primitive'
import { useForwardRef } from '@oku-ui/use-composable'

interface AspectRatioProps extends PrimitiveProps {
ratio?: number
}

type AspectRatioElement = ElementType<'div'>
export type _AspectRatioEl = HTMLDivElement

const NAME = 'AspectRatio'

Expand All @@ -20,13 +21,10 @@ const AspectRatio = defineComponent({
default: 1 / 1,
},
},
setup(props, { attrs, slots, expose }) {
setup(props, { attrs, slots }) {
const { style, ...aspectRatioProps } = attrs as AspectRatioElement
const innerRef = ref<ComponentPublicInstance>()

expose({
innerRef: computed(() => innerRef.value?.$el),
})
const forwardedRef = useForwardRef()

const originalReturn = () => h(
'div', {
Expand All @@ -35,14 +33,14 @@ const AspectRatio = defineComponent({
width: '100%',
paddingBottom: `${100 / props.ratio}%`,
},
'data-radix-aspect-ratio-wrapper': '',
'data-oku-aspect-ratio-wrapper': '',
},
[
h(
Primitive.div,
{
...aspectRatioProps,
ref: innerRef,
ref: forwardedRef,
style: {
...(style as any),
position: 'absolute',
Expand All @@ -59,17 +57,15 @@ const AspectRatio = defineComponent({
],
)

return originalReturn as unknown as {
innerRef: AspectRatioElement
}
return originalReturn
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
type _AspectRatioProps = MergeProps<AspectRatioProps, AspectRatioElement>
type AspectRatioRef = RefElement<typeof AspectRatio>
type InstanceAspectRatioType = InstanceTypeRef<typeof AspectRatio, _AspectRatioEl>

const OkuAspectRatio = AspectRatio as typeof AspectRatio & (new () => { $props: _AspectRatioProps })

export { OkuAspectRatio }
export type { AspectRatioProps, AspectRatioElement, AspectRatioRef }
export type { AspectRatioProps, AspectRatioElement, InstanceAspectRatioType }
3 changes: 2 additions & 1 deletion packages/components/aspect-ratio/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export {
export type {
AspectRatioProps,
AspectRatioElement,
AspectRatioRef,
InstanceAspectRatioType,
_AspectRatioEl,
} from './aspect-ratio'
13 changes: 10 additions & 3 deletions packages/components/aspect-ratio/src/stories/AspectRatioDemo.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import type { AspectRatioProps } from '@oku-ui/aspect-ratio'
import type { AspectRatioProps, InstanceAspectRatioType } from '@oku-ui/aspect-ratio'
import { OkuAspectRatio } from '@oku-ui/aspect-ratio'
import { onMounted, ref } from 'vue'

export interface IAspectRatioProps extends AspectRatioProps {
template?: '#1' | '#2' | '#3' | '#4' | '#5'
Expand All @@ -13,12 +14,18 @@ withDefaults(defineProps<IAspectRatioProps>(), {
template: '#1',
allShow: false,
})

const root = ref<InstanceAspectRatioType>()
onMounted(() => {
// eslint-disable-next-line no-console
console.log(root.value?.$el)
productdevbook marked this conversation as resolved.
Show resolved Hide resolved
})
</script>

<template>
<div class="cursor-default inline-block grid gap-6">
<div class="cursor-default inline-block gap-6">
<div v-if="template === '#1' || allShow" class="w-[300px] rounded-sm overflow-hidden">
<OkuAspectRatio class="bg-cyan-500 flex items-center justify-center text-white font-bold">
<OkuAspectRatio ref="root" class="bg-cyan-500 flex items-center justify-center text-white font-bold">
<h1>Default ratio (1/1)</h1>
</OkuAspectRatio>
</div>
Expand Down
27 changes: 12 additions & 15 deletions packages/components/avatar/src/avatar.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { PropType } from 'vue'
import { computed, defineComponent, h, ref } from 'vue'
import type { ElementType, MergeProps, PrimitiveProps, RefElement } from '@oku-ui/primitive'
import { defineComponent, h, ref } from 'vue'
import type { ElementType, InstanceTypeRef, MergeProps, PrimitiveProps } from '@oku-ui/primitive'
import { Primitive } from '@oku-ui/primitive'
import type { Scope } from '@oku-ui/provide'
import { createProvideScope } from '@oku-ui/provide'
import { useForwardRef } from '@oku-ui/use-composable'

const AVATAR_NAME = 'Avatar'
const [createAvatarProvide, createAvatarScope] = createProvideScope(AVATAR_NAME)
Expand All @@ -18,6 +19,7 @@ type AvatarProvideValue = {
export const [AvatarProvider, useAvatarInject] = createAvatarProvide<AvatarProvideValue>(AVATAR_NAME)

type AvatarElement = ElementType<'span'>
export type _AvatarEl = HTMLSpanElement

interface AvatarProps extends PrimitiveProps {
scopeAvatar?: Scope
Expand All @@ -32,9 +34,11 @@ const Avatar = defineComponent({
required: false,
},
},
setup(props, { attrs, slots, expose }) {
setup(props, { attrs, slots }) {
const { ...avatarProps } = attrs as AvatarElement
const innerRef = ref()

const forwardedRef = useForwardRef()

const imageLoadingStatus = ref<ImageLoadingStatus>('idle')

AvatarProvider({
Expand All @@ -45,29 +49,23 @@ const Avatar = defineComponent({
},
})

expose({
inferRef: computed(() => innerRef.value?.$el),
})

const originalReturn = () => h(
Primitive.span, {
...avatarProps,
ref: innerRef,
ref: forwardedRef,
},
{
default: () => slots.default?.(),
},
)
return originalReturn as unknown as {
innerRef: AvatarElement
}
return originalReturn
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
type _OkuAvatarProps = MergeProps<AvatarProps, AvatarElement>

type AvatarRef = RefElement<typeof Avatar>
type InstanceAvatarType = InstanceTypeRef<typeof Avatar, _AvatarEl>

const OkuAvatar = Avatar as typeof Avatar & (new () => { $props: _OkuAvatarProps })

Expand All @@ -79,6 +77,5 @@ export {
export type {
AvatarProps,
AvatarElement,
AvatarRef,

InstanceAvatarType,
}
Loading
Loading