npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inlayphp/resources-vue

v0.3.11

Published

Vue renderer for Inlay resources and relation managers.

Readme

@inlayphp/resources-vue

npm License

Vue renderer for Inlay resources and relation managers

Vue components for owner-scoped Inlay Relation Managers. They compose the standard Vue Table and Form renderers and keep authorization, validation, queries, and persistence in PHP.

Install

pnpm add @inlayphp/resources @inlayphp/resources-vue @inlayphp/widgets-vue

Render registered managers

<script setup lang="ts">
import { router } from '@inertiajs/vue3'
import {
  RelationManagers,
  type RelationManagerResource,
} from '@inlayphp/resources-vue'

defineProps<{ relations: RelationManagerResource[] }>()
</script>

<template>
  <RelationManagers
    :resources="relations"
    @changed="router.reload({ only: ['relations'] })"
  />
</template>

Use RelationManager for one contract. Props include resource, mutate, and empty; events include changed(operation, record) and query-change(query). Changed operations include create, edit, delete, restore, force-delete, attach, detach, associate, and dissociate. The default same-origin JSON transport sends Laravel CSRF headers and maps 422 errors into the normal Form error contract.

Create, edit, attach, and associate use an accessible modal with close, backdrop, and Escape behavior. Candidate selection uses the normal remote searchable Select. Delete, detach, and dissociate execute through the standard Table action confirmation flow. Additional Attach fields travel through the same Form submission as validated pivot data. Edit dialogs use the dedicated PHP editForm contract and hydrate declared pivot attributes even for edit-only managers. Reload the relations Inertia prop after mutations so policy decisions, query state, and rendered values remain server-authoritative.

PHP RelationGroup::make() metadata is rendered automatically as semantic tabs. The configured default is selected first, with Arrow Left/Right and Home/End keyboard navigation. Vue pages keep passing the ordinary relations prop and do not duplicate the grouping configuration.

Compose a resource page

ResourcePage provides the same page chrome as the React renderer while leaving the actual Form, Table, Infolist, and relation content to slots. Breadcrumbs, record navigation, named views, and PHP ResourcePage::headerActions() remain server-authored.

<script setup lang="ts">
import { ResourcePage } from '@inlayphp/resources-vue'
import { Form } from '@inlayphp/forms-vue'
import type { ResourcePageAction } from '@inlayphp/resources-vue'

defineProps<{
  form: unknown
  errors: Record<string, string>
  breadcrumbs: unknown[]
  heading: string
  headerActions?: ResourcePageAction[]
}>()
</script>

<template>
  <ResourcePage :breadcrumbs="breadcrumbs" :header-actions="headerActions" :heading="heading">
    <!-- Named #actions remains available for app-specific controls. It is
         rendered after server-declared headerActions. -->
    <template #actions><button type="button">Custom control</button></template>
    <Form :errors="errors" :resource="form" />
  </ResourcePage>
</template>

Each PHP action is rendered with @inlayphp/actions-vue's ActionButton and shared runtime. Confirmation modals, action forms, validation errors, keyboard bindings, downloads, and lifecycle responses therefore behave like actions inside Forms and Tables. Ordinary actions use an Inertia visit; actions with a lifecycle handler use the JSON action endpoint. Pass an action-executor prop when an application needs custom transport or reporting:

<ResourcePage
  :action-executor="context => myExecutor(context)"
  :action-input="{ parameters: { view: 'all' } }"
  :header-actions="headerActions"
  :heading="heading"
>
  <Form :errors="errors" :resource="form" />
</ResourcePage>

actionInput is optional and is forwarded to every declared header action. Use it for route parameters, initial action data, or selected records when a custom page renders the same action set in more than one context.

ResourcePageAction and ResourcePageActionExecutor are exported types for page registries and wrapper components.

When tabs is present, listen for tab-select and visit the selected server view. No client-side filtering is performed.

Page widgets

Resource pages can render the PHP-resolved headerWidgets and footerWidgets contracts through the same dashboard renderer used by an Inlay panel. Pass each inlay.widget-dashboard.v1 payload to the matching prop:

<script setup lang="ts">
import { ResourcePage } from '@inlayphp/resources-vue'
import type { WidgetDashboardResource } from '@inlayphp/widgets-vue'

defineProps<{
  heading: string
  headerWidgets?: WidgetDashboardResource | null
  footerWidgets?: WidgetDashboardResource | null
}>()
</script>

<template>
  <ResourcePage
    :footer-widgets="footerWidgets"
    :header-widgets="headerWidgets"
    :heading="heading"
  >
    <Form :resource="form" :errors="errors" />
  </ResourcePage>
</template>

headerWidgets is rendered after the page's tabs and before the before slot; footerWidgets is rendered after the page content and the after slot. Both props are optional, preserve PHP ordering and visibility, and render nothing when the server does not publish a dashboard. widgetProps forwards theme, class-name, icon, custom-renderer, and refresh options to both dashboards. The WidgetDashboard component continues to own responsive spans, empty states, custom renderers, icon registries, polling, and table widgets. Use its @refresh event directly when the page needs to reload a lazy or polling widget:

<ResourcePage
  :header-widgets="headerWidgets"
  :heading="heading"
  :widget-props="{ classNames: { root: 'compact-widgets' }, onRefresh: refreshWidget }"
>
  <template #default><Form :resource="form" :errors="errors" /></template>
</ResourcePage>

widgetProps is applied to both dashboards and accepts theme, className, classNames, icons, and renderers from @inlayphp/widgets-vue. Its onRefresh(name) callback receives the widget name for lazy/polling refreshes; the page decides whether to reload an Inertia prop, fetch JSON, or update its own state. This keeps widget transport out of the renderer and makes theme and component overrides symmetrical with the React resource page.

For custom widget themes or renderers, render WidgetDashboard directly in a named page slot and keep the PHP payload unchanged. Resource-page defaults are deliberately unopinionated so existing action, before, after, and default content slots remain compatible.