Quick Actions
blocklayoutVertical list of clickable shortcuts in a SectionCard. Spell rows out inline with QuickActionItem — swap the anchor for your router Link when you need SPA routing.
Installation
$pnpm dlx shadcn@latest add https://uipkge.dev/r/lit/quick-actions.json$npx shadcn@latest add https://uipkge.dev/r/lit/quick-actions.json$yarn dlx shadcn@latest add https://uipkge.dev/r/lit/quick-actions.json$bunx shadcn@latest add https://uipkge.dev/r/lit/quick-actions.jsonManifest:
https://uipkge.dev/r/lit/quick-actions.jsonInstalls to:src/uip/blocks/quick-actions/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
heading | string | Quick Actions | optional |
description | string | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
HTMLElementTagNameMapinterface HTMLElementTagNameMap {
'uip-quick-actions': UipQuickActions
'uip-quick-action-item': UipQuickActionItem
}
}Includes
Files installed (1)
src/uip/blocks/quick-actions/quick-actions.ts4.1 kB
import { LitElement, css, html, nothing } from 'lit' import { ArrowUpRight, type IconNode } from 'lucide' import * as icons from 'lucide' import { tailwind } from '../../lib/styles' import { ThemeController } from '../../lib/theme' import { icon } from '../../lib/icon' // Side-effect imports: register the composed elements for standalone use // (consumers who install only this block still get working primitives). import '../../components/icon-box/icon-box' import '../../components/section-card/section-card' function toPascal(kebab: string) { return kebab .split('-') .map((p) => (p ? `${p[0] ?? ''}`.toUpperCase() + p.slice(1) : '')) .join('') } /** * <uip-quick-actions> — the registry QuickActions block as a web component. * * Class strings are React's QuickActions / QuickActionItem verbatim. * `title` is `heading` (`title` is taken by HTMLElement); rows are * <uip-quick-action-item> light-DOM children (React's `children`), spaced by * the block's own `flex flex-col gap-2` wrapper (React's `contentClassName`). * Swap the anchor for your router link when you need SPA routing — you own * this file. */ export class UipQuickActions extends LitElement { static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true } // :host display can't be a utility class (the host has no template of its own). static styles = [tailwind, css`:host { display: block; }`] static properties = { heading: {}, description: {}, } heading = 'Quick Actions' description?: string constructor() { super() new ThemeController(this) } connectedCallback() { super.connectedCallback() this.setAttribute('data-uipkge', '') this.setAttribute('data-slot', 'quick-actions') } render() { return html` <uip-section-card heading=${this.heading} description=${this.description ?? nothing}> <div class="flex flex-col gap-2"><slot></slot></div> </uip-section-card> ` } } /** * <uip-quick-action-item> — one shortcut row (React's QuickActionItem). * `label` + `href` render the anchor; the icon is `icon-name` (kebab-case * lucide name, e.g. `user-plus`) or the `icon` IconNode property. */ export class UipQuickActionItem extends LitElement { static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true } // :host display can't be a utility class (the host has no template of its own). static styles = [tailwind, css`:host { display: block; }`] static properties = { label: {}, href: {}, icon: { attribute: false }, iconName: { attribute: 'icon-name' }, } label = '' href?: string icon?: IconNode iconName?: string constructor() { super() new ThemeController(this) } connectedCallback() { super.connectedCallback() this.setAttribute('data-uipkge', '') this.setAttribute('data-slot', 'quick-action-item') } private get resolvedIcon(): IconNode | undefined { if (this.icon) return this.icon if (!this.iconName) return undefined const node = (icons as unknown as Record<string, IconNode>)[toPascal(this.iconName)] return node } render() { const node = this.resolvedIcon return html` <a href=${this.href ?? nothing} class="hover:border-border hover:bg-muted/50 group flex cursor-pointer items-center gap-3 rounded-lg border border-transparent p-3 transition-colors duration-200" > ${node ? html`<uip-icon-box .icon=${node} icon-name=${this.iconName ?? 'icon'}></uip-icon-box>` : nothing} <span class="flex-1 text-sm font-medium">${this.label}</span> ${icon(ArrowUpRight, 'arrow-up-right', 'text-muted-foreground size-4 opacity-0 transition-colors duration-200 group-hover:opacity-100')} </a> ` } } customElements.get('uip-quick-actions') || customElements.define('uip-quick-actions', UipQuickActions) customElements.get('uip-quick-action-item') || customElements.define('uip-quick-action-item', UipQuickActionItem) declare global { interface HTMLElementTagNameMap { 'uip-quick-actions': UipQuickActions 'uip-quick-action-item': UipQuickActionItem } }
Raw manifest:https://uipkge.dev/r/lit/quick-actions.json