diff --git a/packages/react/src/Link.ts b/packages/react/src/Link.ts index 5d29b01f8..9277cdd50 100755 --- a/packages/react/src/Link.ts +++ b/packages/react/src/Link.ts @@ -7,12 +7,12 @@ import { router, shouldIntercept, } from '@inertiajs/core' -import { createElement, forwardRef, useCallback } from 'react' +import React, { ElementType, createElement, forwardRef, useCallback, useEffect, useRef } from 'react' const noop = () => undefined interface BaseInertiaLinkProps { - as?: string + as?: string | ElementType data?: Record href: string method?: Method @@ -115,13 +115,38 @@ const Link = forwardRef( ], ) - as = as.toLowerCase() + const internalRef = useRef(null) + + const combinedRef = (element: HTMLElement | null) => { + internalRef.current = element + + if (!ref) { + return + } + + if (typeof ref === 'function') { + ref(element) + } else { + ref.current = element + } + } + + useEffect(() => { + const element = internalRef.current + + if (element && element.tagName !== 'A') { + element.removeAttribute('href') + } + }, []) + + const isAnchor = as === 'a' || as === 'A' + const isCustomComponent = typeof as !== 'string' method = method.toLowerCase() as Method const [_href, _data] = mergeDataIntoQueryString(method, href || '', data, queryStringArrayFormat) href = _href data = _data - if (as === 'a' && method !== 'get') { + if (isAnchor && method !== 'get') { console.warn( `Creating POST/PUT/PATCH/DELETE links is discouraged as it causes "Open Link in New Tab/Window" accessibility issues.\n\nPlease specify a more appropriate element using the "as" attribute. For example:\n\n...`, ) @@ -131,8 +156,8 @@ const Link = forwardRef( as, { ...props, - ...(as === 'a' ? { href } : {}), - ref, + ...(isAnchor || isCustomComponent ? { href } : {}), + ref: combinedRef, onClick: visit, }, children, diff --git a/packages/vue3/src/link.ts b/packages/vue3/src/link.ts index 1c783aaeb..531ce4c7b 100755 --- a/packages/vue3/src/link.ts +++ b/packages/vue3/src/link.ts @@ -1,8 +1,8 @@ import { mergeDataIntoQueryString, Method, PageProps, Progress, router, shouldIntercept } from '@inertiajs/core' -import { defineComponent, DefineComponent, h, PropType } from 'vue' +import { Component, defineComponent, DefineComponent, h, onMounted, PropType, ref } from 'vue' export interface InertiaLinkProps { - as?: string + as?: string | Component data?: object href: string method?: Method @@ -29,7 +29,7 @@ const Link: InertiaLink = defineComponent({ name: 'Link', props: { as: { - type: String, + type: [String, Object] as PropType, default: 'a', }, data: { @@ -74,12 +74,27 @@ const Link: InertiaLink = defineComponent({ }, }, setup(props, { slots, attrs }) { + const internalRef = ref(null) + + onMounted(() => { + if (!internalRef.value) { + return + } + + const element = internalRef.value.$el + + if (element.tagName !== 'A') { + element.removeAttribute('href') + } + }) + return () => { - const as = props.as.toLowerCase() + const isAnchor = props.as === 'a' || props.as === 'A' + const isCustomComponent = typeof props.as !== 'string' const method = props.method.toLowerCase() as Method const [href, data] = mergeDataIntoQueryString(method, props.href || '', props.data, props.queryStringArrayFormat) - if (as === 'a' && method !== 'get') { + if (isAnchor && method !== 'get') { console.warn( `Creating POST/PUT/PATCH/DELETE links is discouraged as it causes "Open Link in New Tab/Window" accessibility issues.\n\nPlease specify a more appropriate element using the "as" attribute. For example:\n\n...`, ) @@ -89,7 +104,8 @@ const Link: InertiaLink = defineComponent({ props.as, { ...attrs, - ...(as === 'a' ? { href } : {}), + ...(isAnchor || isCustomComponent ? { href } : {}), + ...(isCustomComponent ? { ref: internalRef } : {}), onClick: (event) => { if (shouldIntercept(event)) { event.preventDefault()