release: WRNexusJS 0.2.51
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -28,16 +28,16 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50",
|
||||
"@wrnexus/router": "^0.2.50",
|
||||
"@wrnexus/csr": "^0.2.50",
|
||||
"@wrnexus/compiler": "^0.2.50",
|
||||
"@wrnexus/styles": "^0.2.50",
|
||||
"@wrnexus/dev-server": "^0.2.50",
|
||||
"@wrnexus/ui": "^0.2.50",
|
||||
"@wrnexus/validation": "^0.2.50",
|
||||
"@wrnexus/i18n": "^0.2.50",
|
||||
"@wrnexus/db": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51",
|
||||
"@wrnexus/router": "^0.2.51",
|
||||
"@wrnexus/csr": "^0.2.51",
|
||||
"@wrnexus/compiler": "^0.2.51",
|
||||
"@wrnexus/styles": "^0.2.51",
|
||||
"@wrnexus/dev-server": "^0.2.51",
|
||||
"@wrnexus/ui": "^0.2.51",
|
||||
"@wrnexus/validation": "^0.2.51",
|
||||
"@wrnexus/i18n": "^0.2.51",
|
||||
"@wrnexus/db": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -25,18 +25,18 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50",
|
||||
"@wrnexus/router": "^0.2.50",
|
||||
"@wrnexus/ssr": "^0.2.50",
|
||||
"@wrnexus/csr": "^0.2.50",
|
||||
"@wrnexus/compiler": "^0.2.50",
|
||||
"@wrnexus/styles": "^0.2.50",
|
||||
"@wrnexus/ui": "^0.2.50",
|
||||
"@wrnexus/validation": "^0.2.50",
|
||||
"@wrnexus/i18n": "^0.2.50",
|
||||
"@wrnexus/db": "^0.2.50",
|
||||
"@wrnexus/pubsub": "^0.2.50",
|
||||
"@wrnexus/uploader": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51",
|
||||
"@wrnexus/router": "^0.2.51",
|
||||
"@wrnexus/ssr": "^0.2.51",
|
||||
"@wrnexus/csr": "^0.2.51",
|
||||
"@wrnexus/compiler": "^0.2.51",
|
||||
"@wrnexus/styles": "^0.2.51",
|
||||
"@wrnexus/ui": "^0.2.51",
|
||||
"@wrnexus/validation": "^0.2.51",
|
||||
"@wrnexus/i18n": "^0.2.51",
|
||||
"@wrnexus/db": "^0.2.51",
|
||||
"@wrnexus/pubsub": "^0.2.51",
|
||||
"@wrnexus/uploader": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.2.50"
|
||||
"@wrnexus/native": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,8 +21,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.2.50",
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/compiler": "^0.2.51",
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.2.50"
|
||||
"@wrnexus/uploader": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1 +1,33 @@
|
||||
component ApiEndpointCard { props { method = "GET" path = "/api/example" description = "" } view { <article class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900"><div class="flex flex-wrap items-center gap-3"><span class="rounded-lg px-2.5 py-1 font-mono text-xs font-bold {method === 'GET' ? 'bg-emerald-100 text-emerald-700' : method === 'POST' ? 'bg-blue-100 text-blue-700' : method === 'DELETE' ? 'bg-red-100 text-red-700' : 'bg-amber-100 text-amber-700'}">{method}</span><code class="text-sm font-semibold">{path}</code></div><p data-show="description" class="mt-3 text-sm text-slate-500">{description}</p><div class="mt-4"><slot /></div></article> } }
|
||||
component ApiEndpointCard {
|
||||
props {
|
||||
method = "GET"
|
||||
path = "/api/example"
|
||||
description = ""
|
||||
}
|
||||
|
||||
view {
|
||||
<article
|
||||
class="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-3" >
|
||||
<span
|
||||
class="rounded-lg px-2.5 py-1 font-mono text-xs font-bold {method === 'GET' ? 'bg-emerald-100 text-emerald-700' : method === 'POST' ? 'bg-blue-100 text-blue-700' : method === 'DELETE' ? 'bg-red-100 text-red-700' : 'bg-amber-100 text-amber-700'}"
|
||||
>
|
||||
{method}
|
||||
</span>
|
||||
<code class="text-sm font-semibold">
|
||||
{path}
|
||||
</code>
|
||||
</div>
|
||||
<p
|
||||
data-show="description"
|
||||
class="mt-3 text-sm text-slate-500"
|
||||
>
|
||||
{description}
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
<slot />
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Banner { props { text = "Announcement" href = "" actionLabel = "Learn more" variant = "brand" } view { <aside class="px-4 py-3 text-center text-sm font-medium {variant === 'warning' ? 'bg-amber-400 text-amber-950' : 'bg-violet-600 text-white'}"><span>{text}</span><a data-show="href" href="{href}" class="ml-2 underline underline-offset-4">{actionLabel} →</a></aside> } }
|
||||
component Banner {
|
||||
props {
|
||||
text = "Announcement"
|
||||
href = ""
|
||||
actionLabel = "Learn more"
|
||||
variant = "brand"
|
||||
}
|
||||
view {
|
||||
<aside class="px-4 py-3 text-center text-sm font-medium {variant === 'warning' ? 'bg-amber-400 text-amber-950' : 'bg-violet-600 text-white'}"><span>{text}</span><a data-show="href" href="{href}" class="ml-2 underline underline-offset-4">{actionLabel} →</a></aside>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component Breadcrumbs { props { homeLabel = "Home" homeHref = "/" current = "Current page" } view { <nav aria-label="Breadcrumb"><ol class="flex flex-wrap items-center gap-2 text-sm"><li><a href="{homeHref}" class="text-slate-500 hover:text-violet-600">{homeLabel}</a></li><li aria-hidden="true" class="text-slate-400">/</li><li aria-current="page" class="font-medium text-slate-900 dark:text-white">{current}</li></ol></nav> } }
|
||||
component Breadcrumbs {
|
||||
props {
|
||||
homeLabel = "Home"
|
||||
homeHref = "/"
|
||||
current = "Current page"
|
||||
}
|
||||
view {
|
||||
<nav aria-label="Breadcrumb"><ol class="flex flex-wrap items-center gap-2 text-sm"><li><a href="{homeHref}" class="text-slate-500 hover:text-violet-600">{homeLabel}</a></li><li aria-hidden="true" class="text-slate-400">/</li><li aria-current="page" class="font-medium text-slate-900 dark:text-white">{current}</li></ol></nav>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component CTASection { props { eyebrow = "Ready?" title = "Start building today" description = "" primaryLabel = "Get started" primaryHref = "#" secondaryLabel = "Contact sales" secondaryHref = "#" } view { <section class="overflow-hidden rounded-3xl bg-gradient-to-br from-violet-600 via-violet-700 to-fuchsia-700 px-6 py-12 text-center text-white shadow-2xl sm:px-12 sm:py-16"><p class="text-sm font-bold uppercase tracking-[0.18em] text-violet-100">{eyebrow}</p><h2 class="mx-auto mt-3 max-w-3xl text-3xl font-black sm:text-4xl">{title}</h2><p data-show="description" class="mx-auto mt-4 max-w-2xl text-violet-100">{description}</p><div class="mt-8 flex flex-wrap justify-center gap-3"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-white px-5 font-semibold text-violet-700 shadow-lg transition hover:-translate-y-0.5">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-white/30 px-5 font-semibold text-white transition hover:bg-white/10">{secondaryLabel}</a></div></section> } }
|
||||
component CTASection {
|
||||
props {
|
||||
eyebrow = "Ready?"
|
||||
title = "Start building today"
|
||||
description = ""
|
||||
primaryLabel = "Get started"
|
||||
primaryHref = "#"
|
||||
secondaryLabel = "Contact sales"
|
||||
secondaryHref = "#"
|
||||
}
|
||||
view {
|
||||
<section class="overflow-hidden rounded-3xl bg-gradient-to-br from-violet-600 via-violet-700 to-fuchsia-700 px-6 py-12 text-center text-white shadow-2xl sm:px-12 sm:py-16"><p class="text-sm font-bold uppercase tracking-[0.18em] text-violet-100">{eyebrow}</p><h2 class="mx-auto mt-3 max-w-3xl text-3xl font-black sm:text-4xl">{title}</h2><p data-show="description" class="mx-auto mt-4 max-w-2xl text-violet-100">{description}</p><div class="mt-8 flex flex-wrap justify-center gap-3"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-white px-5 font-semibold text-violet-700 shadow-lg transition hover:-translate-y-0.5">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-white/30 px-5 font-semibold text-white transition hover:bg-white/10">{secondaryLabel}</a></div></section>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Cluster { props { gap = "4" className = "" } view { <div class="flex flex-wrap items-center gap-{gap} {className}"><slot /></div> } }
|
||||
component Cluster {
|
||||
props {
|
||||
gap = "4"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="flex flex-wrap items-center gap-{gap} {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component CodeBlock { props { language = "text" filename = "" code = "" } state copied = false view { <div class="overflow-hidden rounded-2xl border border-slate-800 bg-slate-950 text-slate-100"><div class="flex min-h-11 items-center justify-between border-b border-slate-800 px-4 text-xs text-slate-400"><span>{filename || language}</span><button type="button" @click="navigator.clipboard.writeText(code); copied = true" class="rounded-lg px-2 py-1 hover:bg-slate-800">{copied ? 'Copied' : 'Copy'}</button></div><pre class="overflow-x-auto p-4 text-sm leading-6"><code>{code}</code></pre></div> } }
|
||||
component CodeBlock {
|
||||
props {
|
||||
language = "text"
|
||||
filename = ""
|
||||
code = ""
|
||||
}
|
||||
state copied = false
|
||||
view {
|
||||
<div class="overflow-hidden rounded-2xl border border-slate-800 bg-slate-950 text-slate-100"><div class="flex min-h-11 items-center justify-between border-b border-slate-800 px-4 text-xs text-slate-400"><span>{filename || language}</span><button type="button" @click="navigator.clipboard.writeText(code); copied = true" class="rounded-lg px-2 py-1 hover:bg-slate-800">{copied ? 'Copied' : 'Copy'}</button></div><pre class="overflow-x-auto p-4 text-sm leading-6"><code>{code}</code></pre></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component CookieBanner { props { title = "We use cookies" description = "We use essential cookies and optional analytics to improve your experience." privacyHref = "/privacy" } state visible = true view { <aside data-show="visible" class="fixed inset-x-4 bottom-4 z-50 mx-auto max-w-4xl rounded-3xl border border-slate-200 bg-white/95 p-5 shadow-2xl backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95" role="dialog" aria-label="Cookie preferences"><div class="flex flex-col gap-5 md:flex-row md:items-center"><div class="min-w-0 flex-1"><h2 class="font-bold">{title}</h2><p class="mt-1 text-sm text-slate-500">{description} <a href="{privacyHref}" class="text-violet-600 underline">Privacy policy</a></p></div><div class="flex flex-wrap gap-2"><button @click="visible = false" class="min-h-11 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">Reject optional</button><button @click="visible = false" class="min-h-11 rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">Accept all</button></div></div></aside> } }
|
||||
component CookieBanner {
|
||||
props {
|
||||
title = "We use cookies"
|
||||
description = "We use essential cookies and optional analytics to improve your experience."
|
||||
privacyHref = "/privacy"
|
||||
}
|
||||
state visible = true
|
||||
view {
|
||||
<aside data-show="visible" class="fixed inset-x-4 bottom-4 z-50 mx-auto max-w-4xl rounded-3xl border border-slate-200 bg-white/95 p-5 shadow-2xl backdrop-blur-xl dark:border-slate-800 dark:bg-slate-900/95" role="dialog" aria-label="Cookie preferences"><div class="flex flex-col gap-5 md:flex-row md:items-center"><div class="min-w-0 flex-1"><h2 class="font-bold">{title}</h2><p class="mt-1 text-sm text-slate-500">{description} <a href="{privacyHref}" class="text-violet-600 underline">Privacy policy</a></p></div><div class="flex flex-wrap gap-2"><button @click="visible = false" class="min-h-11 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">Reject optional</button><button @click="visible = false" class="min-h-11 rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">Accept all</button></div></div></aside>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component DateInput {
|
||||
props { id = "" name = "" label = "Date" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Date"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component DatePicker { props { id = "date" name = "date" label = "Date" value = "" min = "" max = "" required = false } view { <DateInput id="{id}" name="{name}" label="{label}" value="{value}" required="{required}" /> } }
|
||||
component DatePicker {
|
||||
props {
|
||||
id = "date"
|
||||
name = "date"
|
||||
label = "Date"
|
||||
value = ""
|
||||
min = ""
|
||||
max = ""
|
||||
required = false
|
||||
}
|
||||
view {
|
||||
<DateInput id="{id}" name="{name}" label="{label}" value="{value}" required="{required}" />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component DateRangePicker { props { label = "Date range" startName = "startDate" endName = "endDate" } view { <fieldset><legend class="mb-2 text-sm font-semibold">{label}</legend><div class="grid gap-3 sm:grid-cols-2"><DateInput id="{startName}" name="{startName}" label="Start date" /><DateInput id="{endName}" name="{endName}" label="End date" /></div></fieldset> } }
|
||||
component DateRangePicker {
|
||||
props {
|
||||
label = "Date range"
|
||||
startName = "startDate"
|
||||
endName = "endDate"
|
||||
}
|
||||
view {
|
||||
<fieldset><legend class="mb-2 text-sm font-semibold">{label}</legend><div class="grid gap-3 sm:grid-cols-2"><DateInput id="{startName}" name="{startName}" label="Start date" /><DateInput id="{endName}" name="{endName}" label="End date" /></div></fieldset>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component DateTimeInput {
|
||||
props { id = "" name = "" label = "DateTime" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "DateTime"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Drawer { props { title = "Panel" open = false side = "right" } state visible = open view { <div data-show="visible" class="fixed inset-0 z-50" role="dialog" aria-modal="true"><button class="absolute inset-0 bg-slate-950/50" aria-label="Close" @click="visible = false"></button><aside class="absolute inset-y-0 w-[min(92vw,28rem)] overflow-auto bg-white p-6 shadow-2xl transition dark:bg-slate-900 {side === 'left' ? 'left-0' : 'right-0'}"><header class="flex items-center justify-between"><h2 class="text-xl font-bold">{title}</h2><button @click="visible = false" class="size-10 rounded-xl hover:bg-slate-100">×</button></header><div class="mt-6"><slot /></div></aside></div> } }
|
||||
component Drawer {
|
||||
props {
|
||||
title = "Panel"
|
||||
open = false
|
||||
side = "right"
|
||||
}
|
||||
state visible = open
|
||||
view {
|
||||
<div data-show="visible" class="fixed inset-0 z-50" role="dialog" aria-modal="true"><button class="absolute inset-0 bg-slate-950/50" aria-label="Close" @click="visible = false"></button><aside class="absolute inset-y-0 w-[min(92vw,28rem)] overflow-auto bg-white p-6 shadow-2xl transition dark:bg-slate-900 {side === 'left' ? 'left-0' : 'right-0'}"><header class="flex items-center justify-between"><h2 class="text-xl font-bold">{title}</h2><button @click="visible = false" class="size-10 rounded-xl hover:bg-slate-100">×</button></header><div class="mt-6"><slot /></div></aside></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component DropdownMenu { props { label = "Menu" } state open = false view { <div class="relative inline-block"><button type="button" aria-haspopup="menu" aria-expanded="{open}" @click="open = !open" class="inline-flex min-h-11 items-center gap-2 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">{label}<span aria-hidden="true">⌄</span></button><div data-show="open" role="menu" class="absolute right-0 z-30 mt-2 min-w-48 rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl dark:border-slate-800 dark:bg-slate-900"><slot /></div></div> } }
|
||||
component DropdownMenu {
|
||||
props {
|
||||
label = "Menu"
|
||||
}
|
||||
state open = false
|
||||
view {
|
||||
<div class="relative inline-block"><button type="button" aria-haspopup="menu" aria-expanded="{open}" @click="open = !open" class="inline-flex min-h-11 items-center gap-2 rounded-xl border border-slate-300 px-4 text-sm font-semibold dark:border-slate-700">{label}<span aria-hidden="true">⌄</span></button><div data-show="open" role="menu" class="absolute right-0 z-30 mt-2 min-w-48 rounded-xl border border-slate-200 bg-white p-1.5 shadow-xl dark:border-slate-800 dark:bg-slate-900"><slot /></div></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component EmailInput {
|
||||
props { id = "" name = "" label = "Email" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Email"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
component EmptyState { props { icon = "◇" title = "Nothing here yet" description = "" actionLabel = "" actionHref = "#" } view { <div class="rounded-3xl border border-dashed border-slate-300 px-6 py-14 text-center dark:border-slate-700"><div class="mx-auto flex size-14 items-center justify-center rounded-2xl bg-violet-100 text-2xl text-violet-700 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-4 text-lg font-bold">{title}</h3><p data-show="description" class="mx-auto mt-2 max-w-md text-sm text-slate-500">{description}</p><a data-show="actionLabel" href="{actionHref}" class="mt-5 inline-flex min-h-11 items-center rounded-xl bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white">{actionLabel}</a></div> } }
|
||||
component EmptyState {
|
||||
props {
|
||||
icon = "◇"
|
||||
title = "Nothing here yet"
|
||||
description = ""
|
||||
actionLabel = ""
|
||||
actionHref = "#"
|
||||
}
|
||||
view {
|
||||
<div class="rounded-3xl border border-dashed border-slate-300 px-6 py-14 text-center dark:border-slate-700"><div class="mx-auto flex size-14 items-center justify-center rounded-2xl bg-violet-100 text-2xl text-violet-700 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-4 text-lg font-bold">{title}</h3><p data-show="description" class="mx-auto mt-2 max-w-md text-sm text-slate-500">{description}</p><a data-show="actionLabel" href="{actionHref}" class="mt-5 inline-flex min-h-11 items-center rounded-xl bg-violet-600 px-4 py-2.5 text-sm font-semibold text-white">{actionLabel}</a></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
component ErrorState { props { code = "500" title = "Something went wrong" description = "Please try again." actionLabel = "Try again" actionHref = "/" } view { <div class="mx-auto max-w-2xl px-4 py-24 text-center"><div class="text-8xl font-black text-violet-100 dark:text-violet-950">{code}</div><h1 class="-mt-4 text-3xl font-black">{title}</h1><p class="mt-4 text-slate-500">{description}</p><a href="{actionHref}" class="mt-8 inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white">{actionLabel}</a></div> } }
|
||||
component ErrorState {
|
||||
props {
|
||||
code = "500"
|
||||
title = "Something went wrong"
|
||||
description = "Please try again."
|
||||
actionLabel = "Try again"
|
||||
actionHref = "/"
|
||||
}
|
||||
view {
|
||||
<div class="mx-auto max-w-2xl px-4 py-24 text-center"><div class="text-8xl font-black text-violet-100 dark:text-violet-950">{code}</div><h1 class="-mt-4 text-3xl font-black">{title}</h1><p class="mt-4 text-slate-500">{description}</p><a href="{actionHref}" class="mt-8 inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white">{actionLabel}</a></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component FeatureCard { props { icon = "✦" title = "Feature" description = "" href = "" } view { <article class="group rounded-2xl border border-slate-200 bg-white p-6 transition duration-300 hover:-translate-y-1 hover:border-violet-300 hover:shadow-xl dark:border-slate-800 dark:bg-slate-900"><div class="flex size-11 items-center justify-center rounded-xl bg-violet-100 text-xl text-violet-700 transition group-hover:scale-110 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-5 text-lg font-bold">{title}</h3><p class="mt-2 leading-7 text-slate-600 dark:text-slate-300">{description}</p><a data-show="href" href="{href}" class="mt-4 inline-flex font-semibold text-violet-600">Learn more →</a></article> } }
|
||||
component FeatureCard {
|
||||
props {
|
||||
icon = "✦"
|
||||
title = "Feature"
|
||||
description = ""
|
||||
href = ""
|
||||
}
|
||||
view {
|
||||
<article class="group rounded-2xl border border-slate-200 bg-white p-6 transition duration-300 hover:-translate-y-1 hover:border-violet-300 hover:shadow-xl dark:border-slate-800 dark:bg-slate-900"><div class="flex size-11 items-center justify-center rounded-xl bg-violet-100 text-xl text-violet-700 transition group-hover:scale-110 dark:bg-violet-950 dark:text-violet-300">{icon}</div><h3 class="mt-5 text-lg font-bold">{title}</h3><p class="mt-2 leading-7 text-slate-600 dark:text-slate-300">{description}</p><a data-show="href" href="{href}" class="mt-4 inline-flex font-semibold text-violet-600">Learn more →</a></article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component FeatureGrid { props { columns = 3 className = "" } view { <div class="grid gap-5 {columns === 2 ? 'md:grid-cols-2' : columns === 4 ? 'sm:grid-cols-2 xl:grid-cols-4' : 'md:grid-cols-2 lg:grid-cols-3'} {className}"><slot /></div> } }
|
||||
component FeatureGrid {
|
||||
props {
|
||||
columns = 3
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="grid gap-5 {columns === 2 ? 'md:grid-cols-2' : columns === 4 ? 'sm:grid-cols-2 xl:grid-cols-4' : 'md:grid-cols-2 lg:grid-cols-3'} {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
component FileUpload { props { id = "file" name = "file" label = "Upload file" accept = "" multiple = false help = "Drag and drop or browse" } view { <label for="{id}" class="flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-2xl border-2 border-dashed border-slate-300 bg-slate-50 p-6 text-center transition hover:border-violet-500 hover:bg-violet-50/50 focus-within:ring-4 focus-within:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-900/50"><span class="text-3xl" aria-hidden="true">⇧</span><span class="mt-2 font-semibold">{label}</span><span class="mt-1 text-sm text-slate-500">{help}</span><input id="{id}" name="{name}" type="file" accept="{accept}" multiple="{multiple}" class="sr-only" /></label> } }
|
||||
component FileUpload {
|
||||
props {
|
||||
id = "file"
|
||||
name = "file"
|
||||
label = "Upload file"
|
||||
accept = ""
|
||||
multiple = false
|
||||
help = "Drag and drop or browse"
|
||||
}
|
||||
view {
|
||||
<label for="{id}" class="flex min-h-40 cursor-pointer flex-col items-center justify-center rounded-2xl border-2 border-dashed border-slate-300 bg-slate-50 p-6 text-center transition hover:border-violet-500 hover:bg-violet-50/50 focus-within:ring-4 focus-within:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-900/50"><span class="text-3xl" aria-hidden="true">⇧</span><span class="mt-2 font-semibold">{label}</span><span class="mt-1 text-sm text-slate-500">{help}</span><input id="{id}" name="{name}" type="file" accept="{accept}" multiple="{multiple}" class="sr-only" /></label>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component FormErrorSummary { props { title = "Please fix the following" visible = true } view { <section data-show="visible" role="alert" aria-live="polite" class="rounded-2xl border border-red-200 bg-red-50 p-4 text-red-900 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200"><h3 class="font-semibold">{title}</h3><div class="mt-2 text-sm"><slot /></div></section> } }
|
||||
component FormErrorSummary {
|
||||
props {
|
||||
title = "Please fix the following"
|
||||
visible = true
|
||||
}
|
||||
view {
|
||||
<section data-show="visible" role="alert" aria-live="polite" class="rounded-2xl border border-red-200 bg-red-50 p-4 text-red-900 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200"><h3 class="font-semibold">{title}</h3><div class="mt-2 text-sm"><slot /></div></section>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component FormField { props { label = "Field" help = "" error = "" required = false } view { <div class="space-y-2"><div class="text-sm font-semibold">{label}<span data-show="required" class="ml-1 text-red-500">*</span></div><slot /><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div> } }
|
||||
component FormField {
|
||||
props {
|
||||
label = "Field"
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2"><div class="text-sm font-semibold">{label}<span data-show="required" class="ml-1 text-red-500">*</span></div><slot /><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,16 @@
|
||||
component Hero { props { eyebrow = "" title = "Build faster with WRNexusJS" highlight = "" description = "" primaryLabel = "Get started" primaryHref = "#" secondaryLabel = "Learn more" secondaryHref = "#" align = "center" } view { <section class="relative isolate overflow-hidden py-20 sm:py-28"><div class="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,rgba(124,58,237,.18),transparent_45%)]"></div><div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"><div class="max-w-4xl {align === 'center' ? 'mx-auto text-center' : ''}"><Badge data-show="eyebrow" label="{eyebrow}" variant="brand" /><h1 class="mt-6 text-4xl font-black tracking-tight text-slate-950 sm:text-6xl dark:text-white">{title} <span data-show="highlight" class="bg-gradient-to-r from-violet-600 to-fuchsia-500 bg-clip-text text-transparent">{highlight}</span></h1><p data-show="description" class="mt-6 text-lg leading-8 text-slate-600 sm:text-xl dark:text-slate-300">{description}</p><div class="mt-8 flex flex-wrap gap-3 {align === 'center' ? 'justify-center' : ''}"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-700">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-slate-300 bg-white px-5 font-semibold text-slate-900 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-white">{secondaryLabel}</a></div><div class="mt-10"><slot /></div></div></div></section> } }
|
||||
component Hero {
|
||||
props {
|
||||
eyebrow = ""
|
||||
title = "Build faster with WRNexusJS"
|
||||
highlight = ""
|
||||
description = ""
|
||||
primaryLabel = "Get started"
|
||||
primaryHref = "#"
|
||||
secondaryLabel = "Learn more"
|
||||
secondaryHref = "#"
|
||||
align = "center"
|
||||
}
|
||||
view {
|
||||
<section class="relative isolate overflow-hidden py-20 sm:py-28"><div class="absolute inset-0 -z-10 bg-[radial-gradient(circle_at_top,rgba(124,58,237,.18),transparent_45%)]"></div><div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"><div class="max-w-4xl {align === 'center' ? 'mx-auto text-center' : ''}"><Badge data-show="eyebrow" label="{eyebrow}" variant="brand" /><h1 class="mt-6 text-4xl font-black tracking-tight text-slate-950 sm:text-6xl dark:text-white">{title} <span data-show="highlight" class="bg-gradient-to-r from-violet-600 to-fuchsia-500 bg-clip-text text-transparent">{highlight}</span></h1><p data-show="description" class="mt-6 text-lg leading-8 text-slate-600 sm:text-xl dark:text-slate-300">{description}</p><div class="mt-8 flex flex-wrap gap-3 {align === 'center' ? 'justify-center' : ''}"><a href="{primaryHref}" class="inline-flex min-h-12 items-center rounded-xl bg-violet-600 px-5 font-semibold text-white shadow-lg shadow-violet-600/20 transition hover:-translate-y-0.5 hover:bg-violet-700">{primaryLabel}</a><a data-show="secondaryLabel" href="{secondaryHref}" class="inline-flex min-h-12 items-center rounded-xl border border-slate-300 bg-white px-5 font-semibold text-slate-900 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-white">{secondaryLabel}</a></div><div class="mt-10"><slot /></div></div></div></section>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
component IconButton {
|
||||
props { label = "Action" icon = "•" variant = "ghost" disabled = false }
|
||||
props {
|
||||
label = "Action"
|
||||
icon = "•"
|
||||
variant = "ghost"
|
||||
disabled = false
|
||||
}
|
||||
view { <button type="button" aria-label="{label}" title="{label}" disabled="{disabled}" class="inline-flex size-11 items-center justify-center rounded-xl transition duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50 {variant === 'primary' ? 'bg-violet-600 text-white hover:bg-violet-700' : 'text-slate-600 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-800'}"><span aria-hidden="true">{icon}</span></button> }
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Inline { props { gap = "4" className = "" } view { <div class="flex flex-wrap items-center gap-{gap} {className}"><slot /></div> } }
|
||||
component Inline {
|
||||
props {
|
||||
gap = "4"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="flex flex-wrap items-center gap-{gap} {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component LegalDocumentLayout { props { title = "Legal document" effectiveDate = "" updatedDate = "" } view { <article class="mx-auto grid max-w-7xl gap-10 px-4 py-14 lg:grid-cols-[16rem_minmax(0,1fr)] lg:px-8"><aside class="lg:sticky lg:top-24 lg:self-start"><div class="text-sm font-semibold">On this page</div><nav class="mt-4 text-sm text-slate-500"><slot name="toc" /></nav></aside><main class="min-w-0 max-w-3xl"><header class="border-b border-slate-200 pb-8 dark:border-slate-800"><h1 class="text-4xl font-black tracking-tight">{title}</h1><div class="mt-4 flex flex-wrap gap-4 text-sm text-slate-500"><span data-show="effectiveDate">Effective: {effectiveDate}</span><span data-show="updatedDate">Updated: {updatedDate}</span></div></header><div class="prose prose-slate mt-8 max-w-none dark:prose-invert"><slot /></div></main></article> } }
|
||||
component LegalDocumentLayout {
|
||||
props {
|
||||
title = "Legal document"
|
||||
effectiveDate = ""
|
||||
updatedDate = ""
|
||||
}
|
||||
view {
|
||||
<article class="mx-auto grid max-w-7xl gap-10 px-4 py-14 lg:grid-cols-[16rem_minmax(0,1fr)] lg:px-8"><aside class="lg:sticky lg:top-24 lg:self-start"><div class="text-sm font-semibold">On this page</div><nav class="mt-4 text-sm text-slate-500"><slot name="toc" /></nav></aside><main class="min-w-0 max-w-3xl"><header class="border-b border-slate-200 pb-8 dark:border-slate-800"><h1 class="text-4xl font-black tracking-tight">{title}</h1><div class="mt-4 flex flex-wrap gap-4 text-sm text-slate-500"><span data-show="effectiveDate">Effective: {effectiveDate}</span><span data-show="updatedDate">Updated: {updatedDate}</span></div></header><div class="prose prose-slate mt-8 max-w-none dark:prose-invert"><slot /></div></main></article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Link { props { href = "#" label = "Link" external = false className = "" } view { <a href="{href}" target="{external ? '_blank' : ''}" rel="{external ? 'noreferrer' : ''}" class="font-medium text-violet-600 underline-offset-4 transition hover:text-violet-700 hover:underline focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 {className}">{label}<span data-show="external" aria-hidden="true"> ↗</span></a> } }
|
||||
component Link {
|
||||
props {
|
||||
href = "#"
|
||||
label = "Link"
|
||||
external = false
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<a href="{href}" target="{external ? '_blank' : ''}" rel="{external ? 'noreferrer' : ''}" class="font-medium text-violet-600 underline-offset-4 transition hover:text-violet-700 hover:underline focus-visible:rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 {className}">{label}<span data-show="external" aria-hidden="true"> ↗</span></a>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component LogoCloud { props { title = "Trusted by modern teams" } view { <section aria-label="{title}"><p class="text-center text-sm font-semibold uppercase tracking-wider text-slate-500">{title}</p><div class="mt-6 flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale transition hover:grayscale-0"><slot /></div></section> } }
|
||||
component LogoCloud {
|
||||
props {
|
||||
title = "Trusted by modern teams"
|
||||
}
|
||||
view {
|
||||
<section aria-label="{title}"><p class="text-center text-sm font-semibold uppercase tracking-wider text-slate-500">{title}</p><div class="mt-6 flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale transition hover:grayscale-0"><slot /></div></section>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
component Modal { props { title = "Dialog" description = "" open = false size = "md" closeLabel = "Close" } state visible = open view { <div data-show="visible" class="fixed inset-0 z-50 flex items-end justify-center p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="absolute inset-0 bg-slate-950/60 backdrop-blur-sm" aria-label="{closeLabel}" @click="visible = false"></button><section class="relative max-h-[90vh] w-full overflow-auto rounded-3xl border border-slate-200 bg-white p-6 shadow-2xl dark:border-slate-800 dark:bg-slate-900 {size === 'sm' ? 'max-w-md' : size === 'lg' ? 'max-w-4xl' : 'max-w-2xl'}"><header class="flex items-start justify-between gap-4"><div><h2 id="modal-title" class="text-xl font-bold">{title}</h2><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="{closeLabel}" @click="visible = false" class="inline-flex size-10 items-center justify-center rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">×</button></header><div class="mt-5"><slot /></div></section></div> } }
|
||||
component Modal {
|
||||
props {
|
||||
title = "Dialog"
|
||||
description = ""
|
||||
open = false
|
||||
size = "md"
|
||||
closeLabel = "Close"
|
||||
}
|
||||
state visible = open
|
||||
view {
|
||||
<div data-show="visible" class="fixed inset-0 z-50 flex items-end justify-center p-4 sm:items-center" role="dialog" aria-modal="true" aria-labelledby="modal-title"><button class="absolute inset-0 bg-slate-950/60 backdrop-blur-sm" aria-label="{closeLabel}" @click="visible = false"></button><section class="relative max-h-[90vh] w-full overflow-auto rounded-3xl border border-slate-200 bg-white p-6 shadow-2xl dark:border-slate-800 dark:bg-slate-900 {size === 'sm' ? 'max-w-md' : size === 'lg' ? 'max-w-4xl' : 'max-w-2xl'}"><header class="flex items-start justify-between gap-4"><div><h2 id="modal-title" class="text-xl font-bold">{title}</h2><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="{closeLabel}" @click="visible = false" class="inline-flex size-10 items-center justify-center rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">×</button></header><div class="mt-5"><slot /></div></section></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component NumberInput {
|
||||
props { id = "" name = "" label = "Number" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Number"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component OtpInput { props { name = "otp" length = 6 label = "Verification code" } view { <fieldset><legend class="mb-3 text-sm font-semibold">{label}</legend><input name="{name}" inputmode="numeric" autocomplete="one-time-code" maxlength="{length}" pattern="[0-9]*" class="min-h-14 w-full rounded-2xl border border-slate-300 bg-white px-4 text-center font-mono text-2xl tracking-[0.5em] outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950" /></fieldset> } }
|
||||
component OtpInput {
|
||||
props {
|
||||
name = "otp"
|
||||
length = 6
|
||||
label = "Verification code"
|
||||
}
|
||||
view {
|
||||
<fieldset><legend class="mb-3 text-sm font-semibold">{label}</legend><input name="{name}" inputmode="numeric" autocomplete="one-time-code" maxlength="{length}" pattern="[0-9]*" class="min-h-14 w-full rounded-2xl border border-slate-300 bg-white px-4 text-center font-mono text-2xl tracking-[0.5em] outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950" /></fieldset>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component PageShell { props { className = "" } view { <div class="min-h-screen bg-white text-slate-950 antialiased selection:bg-violet-200 dark:bg-slate-950 dark:text-white dark:selection:bg-violet-800 {className}"><slot /></div> } }
|
||||
component PageShell {
|
||||
props {
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="min-h-screen bg-white text-slate-950 antialiased selection:bg-violet-200 dark:bg-slate-950 dark:text-white dark:selection:bg-violet-800 {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Pagination { props { page = 1 totalPages = 1 previousHref = "#" nextHref = "#" } view { <nav aria-label="Pagination" class="flex items-center justify-between gap-4"><a href="{previousHref}" aria-disabled="{page <= 1}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">← Previous</a><span class="text-sm text-slate-500">Page {page} of {totalPages}</span><a href="{nextHref}" aria-disabled="{page >= totalPages}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">Next →</a></nav> } }
|
||||
component Pagination {
|
||||
props {
|
||||
page = 1
|
||||
totalPages = 1
|
||||
previousHref = "#"
|
||||
nextHref = "#"
|
||||
}
|
||||
view {
|
||||
<nav aria-label="Pagination" class="flex items-center justify-between gap-4"><a href="{previousHref}" aria-disabled="{page <= 1}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">← Previous</a><span class="text-sm text-slate-500">Page {page} of {totalPages}</span><a href="{nextHref}" aria-disabled="{page >= totalPages}" class="inline-flex min-h-11 items-center rounded-xl border border-slate-300 px-4 text-sm font-semibold aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:border-slate-700">Next →</a></nav>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component PasswordInput {
|
||||
props { id = "" name = "" label = "Password" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Password"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component PhoneInput {
|
||||
props { id = "" name = "" label = "Phone" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Phone"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component PricingPlanCard { props { name = "Starter" description = "" price = "₹0" period = "/month" featured = false ctaLabel = "Choose plan" ctaHref = "#" } view { <article class="relative flex h-full flex-col rounded-3xl border p-6 shadow-sm {featured ? 'border-violet-500 bg-violet-50/50 ring-2 ring-violet-500 dark:bg-violet-950/20' : 'border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900'}"><Badge data-show="featured" label="Most popular" variant="brand" /><h3 class="mt-4 text-xl font-bold">{name}</h3><p class="mt-2 text-sm text-slate-500">{description}</p><div class="mt-6"><span class="text-4xl font-black">{price}</span><span class="text-slate-500">{period}</span></div><div class="mt-6 flex-1"><slot /></div><a href="{ctaHref}" class="mt-6 inline-flex min-h-12 items-center justify-center rounded-xl font-semibold transition {featured ? 'bg-violet-600 text-white hover:bg-violet-700' : 'border border-slate-300 hover:bg-slate-50 dark:border-slate-700'}">{ctaLabel}</a></article> } }
|
||||
component PricingPlanCard {
|
||||
props {
|
||||
name = "Starter"
|
||||
description = ""
|
||||
price = "₹0"
|
||||
period = "/month"
|
||||
featured = false
|
||||
ctaLabel = "Choose plan"
|
||||
ctaHref = "#"
|
||||
}
|
||||
view {
|
||||
<article class="relative flex h-full flex-col rounded-3xl border p-6 shadow-sm {featured ? 'border-violet-500 bg-violet-50/50 ring-2 ring-violet-500 dark:bg-violet-950/20' : 'border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900'}"><Badge data-show="featured" label="Most popular" variant="brand" /><h3 class="mt-4 text-xl font-bold">{name}</h3><p class="mt-2 text-sm text-slate-500">{description}</p><div class="mt-6"><span class="text-4xl font-black">{price}</span><span class="text-slate-500">{period}</span></div><div class="mt-6 flex-1"><slot /></div><a href="{ctaHref}" class="mt-6 inline-flex min-h-12 items-center justify-center rounded-xl font-semibold transition {featured ? 'bg-violet-600 text-white hover:bg-violet-700' : 'border border-slate-300 hover:bg-slate-50 dark:border-slate-700'}">{ctaLabel}</a></article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component ProgressBar { props { value = 0 max = 100 label = "Progress" showValue = true } view { <div><div class="mb-2 flex justify-between text-sm"><span>{label}</span><span data-show="showValue">{value}%</span></div><div class="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800" role="progressbar" aria-valuenow="{value}" aria-valuemin="0" aria-valuemax="{max}"><div class="h-full rounded-full bg-violet-600 transition-all duration-500" style="width: {value}%"></div></div></div> } }
|
||||
component ProgressBar {
|
||||
props {
|
||||
value = 0
|
||||
max = 100
|
||||
label = "Progress"
|
||||
showValue = true
|
||||
}
|
||||
view {
|
||||
<div><div class="mb-2 flex justify-between text-sm"><span>{label}</span><span data-show="showValue">{value}%</span></div><div class="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800" role="progressbar" aria-valuenow="{value}" aria-valuemin="0" aria-valuemax="{max}"><div class="h-full rounded-full bg-violet-600 transition-all duration-500" style="width: {value}%"></div></div></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component PublicFooter { props { brand = "WRNexusJS" description = "SSR-first, Bun-native full-stack framework." copyright = "© 2026 WorkRoot Workspace" } view { <footer class="border-t border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-950"><div class="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:px-8"><div class="grid gap-10 lg:grid-cols-[1.2fr_2fr]"><div><div class="text-xl font-black">{brand}</div><p class="mt-3 max-w-sm text-sm leading-6 text-slate-500">{description}</p></div><div class="grid grid-cols-2 gap-8 sm:grid-cols-4"><slot /></div></div><div class="mt-12 flex flex-col gap-4 border-t border-slate-200 pt-6 text-sm text-slate-500 sm:flex-row sm:items-center sm:justify-between dark:border-slate-800"><span>{copyright}</span><button data-wire-theme-toggle type="button" class="min-h-10 rounded-xl border border-slate-300 px-3 dark:border-slate-700">Toggle theme</button></div></div></footer> } }
|
||||
component PublicFooter {
|
||||
props {
|
||||
brand = "WRNexusJS"
|
||||
description = "SSR-first, Bun-native full-stack framework."
|
||||
copyright = "© 2026 WorkRoot Workspace"
|
||||
}
|
||||
view {
|
||||
<footer class="border-t border-slate-200 bg-slate-50 dark:border-slate-800 dark:bg-slate-950"><div class="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:px-8"><div class="grid gap-10 lg:grid-cols-[1.2fr_2fr]"><div><div class="text-xl font-black">{brand}</div><p class="mt-3 max-w-sm text-sm leading-6 text-slate-500">{description}</p></div><div class="grid grid-cols-2 gap-8 sm:grid-cols-4"><slot /></div></div><div class="mt-12 flex flex-col gap-4 border-t border-slate-200 pt-6 text-sm text-slate-500 sm:flex-row sm:items-center sm:justify-between dark:border-slate-800"><span>{copyright}</span><button data-wire-theme-toggle type="button" class="min-h-10 rounded-xl border border-slate-300 px-3 dark:border-slate-700">Toggle theme</button></div></div></footer>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
component PublicHeader { props { brand = "WRNexusJS" homeHref = "/" signInHref = "/signin" ctaHref = "/start" ctaLabel = "Get started" } state mobileOpen = false view { <header class="sticky top-0 z-40 border-b border-slate-200/80 bg-white/85 backdrop-blur-xl dark:border-slate-800 dark:bg-slate-950/85"><div class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8"><a href="{homeHref}" class="text-lg font-black tracking-tight">{brand}</a><nav class="hidden items-center gap-7 md:flex" aria-label="Primary"><slot /></nav><div class="hidden items-center gap-3 md:flex"><a href="{signInHref}" class="text-sm font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-10 items-center rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">{ctaLabel}</a><button data-wire-theme-toggle type="button" aria-label="Toggle theme" class="size-10 rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">◐</button></div><button type="button" aria-label="Open menu" aria-expanded="{mobileOpen}" @click="mobileOpen = !mobileOpen" class="size-11 rounded-xl md:hidden">☰</button></div><div data-show="mobileOpen" class="border-t border-slate-200 px-4 py-5 md:hidden dark:border-slate-800"><div class="space-y-3"><slot name="mobile" /></div><div class="mt-5 grid grid-cols-2 gap-3"><a href="{signInHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl border border-slate-300 font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl bg-violet-600 font-semibold text-white">{ctaLabel}</a></div></div></header> } }
|
||||
component PublicHeader {
|
||||
props {
|
||||
brand = "WRNexusJS"
|
||||
homeHref = "/"
|
||||
signInHref = "/signin"
|
||||
ctaHref = "/start"
|
||||
ctaLabel = "Get started"
|
||||
}
|
||||
state mobileOpen = false
|
||||
view {
|
||||
<header class="sticky top-0 z-40 border-b border-slate-200/80 bg-white/85 backdrop-blur-xl dark:border-slate-800 dark:bg-slate-950/85"><div class="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8"><a href="{homeHref}" class="text-lg font-black tracking-tight">{brand}</a><nav class="hidden items-center gap-7 md:flex" aria-label="Primary"><slot /></nav><div class="hidden items-center gap-3 md:flex"><a href="{signInHref}" class="text-sm font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-10 items-center rounded-xl bg-violet-600 px-4 text-sm font-semibold text-white">{ctaLabel}</a><button data-wire-theme-toggle type="button" aria-label="Toggle theme" class="size-10 rounded-xl hover:bg-slate-100 dark:hover:bg-slate-800">◐</button></div><button type="button" aria-label="Open menu" aria-expanded="{mobileOpen}" @click="mobileOpen = !mobileOpen" class="size-11 rounded-xl md:hidden">☰</button></div><div data-show="mobileOpen" class="border-t border-slate-200 px-4 py-5 md:hidden dark:border-slate-800"><div class="space-y-3"><slot name="mobile" /></div><div class="mt-5 grid grid-cols-2 gap-3"><a href="{signInHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl border border-slate-300 font-semibold">Sign in</a><a href="{ctaHref}" class="inline-flex min-h-11 items-center justify-center rounded-xl bg-violet-600 font-semibold text-white">{ctaLabel}</a></div></div></header>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component RadioGroup { props { label = "Choose one" name = "choice" } view { <fieldset class="space-y-3"><legend class="text-sm font-semibold text-slate-900 dark:text-white">{label}</legend><div class="space-y-2"><slot /></div></fieldset> } }
|
||||
component RadioGroup {
|
||||
props {
|
||||
label = "Choose one"
|
||||
name = "choice"
|
||||
}
|
||||
view {
|
||||
<fieldset class="space-y-3"><legend class="text-sm font-semibold text-slate-900 dark:text-white">{label}</legend><div class="space-y-2"><slot /></div></fieldset>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component SchedulePicker { props { label = "Schedule" } view { <fieldset class="space-y-4 rounded-2xl border border-slate-200 p-5 dark:border-slate-800"><legend class="px-2 font-semibold">{label}</legend><DateTimeInput id="scheduleAt" name="scheduleAt" label="Date and time" /><TimezoneSelector /></fieldset> } }
|
||||
component SchedulePicker {
|
||||
props {
|
||||
label = "Schedule"
|
||||
}
|
||||
view {
|
||||
<fieldset class="space-y-4 rounded-2xl border border-slate-200 p-5 dark:border-slate-800"><legend class="px-2 font-semibold">{label}</legend><DateTimeInput id="scheduleAt" name="scheduleAt" label="Date and time" /><TimezoneSelector /></fieldset>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component SearchInput {
|
||||
props { id = "" name = "" label = "Search" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Search"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
component Section {
|
||||
props { id = "" size = "default" surface = "default" className = "" }
|
||||
props {
|
||||
id = ""
|
||||
size = "default"
|
||||
surface = "default"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<section id="{id}" class="{size === 'compact' ? 'py-10 sm:py-14' : size === 'spacious' ? 'py-20 sm:py-28' : 'py-14 sm:py-20'} {surface === 'muted' ? 'bg-slate-50 dark:bg-slate-900/50' : surface === 'brand' ? 'bg-violet-600 text-white' : ''} {className}"><slot /></section>
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component SectionHeader { props { eyebrow = "" title = "Section title" description = "" align = "left" } view { <header class="max-w-3xl {align === 'center' ? 'mx-auto text-center' : ''}"><p data-show="eyebrow" class="mb-3 text-sm font-bold uppercase tracking-[0.18em] text-violet-600 dark:text-violet-400">{eyebrow}</p><h2 class="text-3xl font-bold tracking-tight text-slate-950 sm:text-4xl dark:text-white">{title}</h2><p data-show="description" class="mt-4 text-lg leading-8 text-slate-600 dark:text-slate-300">{description}</p><div class="mt-6"><slot /></div></header> } }
|
||||
component SectionHeader {
|
||||
props {
|
||||
eyebrow = ""
|
||||
title = "Section title"
|
||||
description = ""
|
||||
align = "left"
|
||||
}
|
||||
view {
|
||||
<header class="max-w-3xl {align === 'center' ? 'mx-auto text-center' : ''}"><p data-show="eyebrow" class="mb-3 text-sm font-bold uppercase tracking-[0.18em] text-violet-600 dark:text-violet-400">{eyebrow}</p><h2 class="text-3xl font-bold tracking-tight text-slate-950 sm:text-4xl dark:text-white">{title}</h2><p data-show="description" class="mt-4 text-lg leading-8 text-slate-600 dark:text-slate-300">{description}</p><div class="mt-6"><slot /></div></header>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component StatCard { props { label = "Metric" value = "0" change = "" trend = "neutral" } view { <article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900"><p class="text-sm font-medium text-slate-500">{label}</p><div class="mt-2 flex items-end justify-between gap-3"><p class="text-3xl font-bold tracking-tight">{value}</p><span data-show="change" class="text-sm font-semibold {trend === 'up' ? 'text-emerald-600' : trend === 'down' ? 'text-red-600' : 'text-slate-500'}">{trend === 'up' ? '↑' : trend === 'down' ? '↓' : ''} {change}</span></div></article> } }
|
||||
component StatCard {
|
||||
props {
|
||||
label = "Metric"
|
||||
value = "0"
|
||||
change = ""
|
||||
trend = "neutral"
|
||||
}
|
||||
view {
|
||||
<article class="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900"><p class="text-sm font-medium text-slate-500">{label}</p><div class="mt-2 flex items-end justify-between gap-3"><p class="text-3xl font-bold tracking-tight">{value}</p><span data-show="change" class="text-sm font-semibold {trend === 'up' ? 'text-emerald-600' : trend === 'down' ? 'text-red-600' : 'text-slate-500'}">{trend === 'up' ? '↑' : trend === 'down' ? '↓' : ''} {change}</span></div></article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component StatusBadge { props { status = "operational" } view { <span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {status === 'operational' ? 'bg-emerald-100 text-emerald-700' : status === 'degraded' ? 'bg-amber-100 text-amber-700' : status === 'outage' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}"><span class="size-2 rounded-full bg-current"></span>{status}</span> } }
|
||||
component StatusBadge {
|
||||
props {
|
||||
status = "operational"
|
||||
}
|
||||
view {
|
||||
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {status === 'operational' ? 'bg-emerald-100 text-emerald-700' : status === 'degraded' ? 'bg-amber-100 text-amber-700' : status === 'outage' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}"><span class="size-2 rounded-full bg-current"></span>{status}</span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Tabs { props { active = "first" } state current = active view { <div><div role="tablist" class="flex gap-1 overflow-x-auto rounded-xl bg-slate-100 p-1 dark:bg-slate-800"><button role="tab" aria-selected="{current === 'first'}" @click="current = 'first'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'first' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">First</button><button role="tab" aria-selected="{current === 'second'}" @click="current = 'second'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'second' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">Second</button></div><div data-show="current === 'first'" role="tabpanel" class="pt-5"><slot name="first" /></div><div data-show="current === 'second'" role="tabpanel" class="pt-5"><slot name="second" /></div></div> } }
|
||||
component Tabs {
|
||||
props {
|
||||
active = "first"
|
||||
}
|
||||
state current = active
|
||||
view {
|
||||
<div><div role="tablist" class="flex gap-1 overflow-x-auto rounded-xl bg-slate-100 p-1 dark:bg-slate-800"><button role="tab" aria-selected="{current === 'first'}" @click="current = 'first'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'first' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">First</button><button role="tab" aria-selected="{current === 'second'}" @click="current = 'second'" class="min-h-10 flex-1 rounded-lg px-4 text-sm font-semibold transition {current === 'second' ? 'bg-white shadow dark:bg-slate-900' : 'text-slate-500'}">Second</button></div><div data-show="current === 'first'" role="tabpanel" class="pt-5"><slot name="first" /></div><div data-show="current === 'second'" role="tabpanel" class="pt-5"><slot name="second" /></div></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
component TestimonialCard { props { quote = "Great product." name = "Customer" role = "" company = "" avatar = "" } view { <figure class="rounded-3xl border border-slate-200 bg-white p-7 shadow-sm dark:border-slate-800 dark:bg-slate-900"><blockquote class="text-lg leading-8 text-slate-700 dark:text-slate-200">“{quote}”</blockquote><figcaption class="mt-6 flex items-center gap-3"><Avatar src="{avatar}" initials="{name.slice(0,2)}" /><div><div class="font-semibold">{name}</div><div class="text-sm text-slate-500">{role}<span data-show="company"> · {company}</span></div></div></figcaption></figure> } }
|
||||
component TestimonialCard {
|
||||
props {
|
||||
quote = "Great product."
|
||||
name = "Customer"
|
||||
role = ""
|
||||
company = ""
|
||||
avatar = ""
|
||||
}
|
||||
view {
|
||||
<figure class="rounded-3xl border border-slate-200 bg-white p-7 shadow-sm dark:border-slate-800 dark:bg-slate-900"><blockquote class="text-lg leading-8 text-slate-700 dark:text-slate-200">“{quote}”</blockquote><figcaption class="mt-6 flex items-center gap-3"><Avatar src="{avatar}" initials="{name.slice(0,2)}" /><div><div class="font-semibold">{name}</div><div class="text-sm text-slate-500">{role}<span data-show="company"> · {company}</span></div></div></figcaption></figure>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component TextInput {
|
||||
props { id = "" name = "" label = "Text" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Text"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component TimeInput {
|
||||
props { id = "" name = "" label = "Time" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Time"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component Timeline { props { title = "Timeline" } view { <section><h2 class="text-lg font-bold">{title}</h2><ol class="mt-5 border-l border-slate-200 pl-6 dark:border-slate-800"><slot /></ol></section> } }
|
||||
component Timeline {
|
||||
props {
|
||||
title = "Timeline"
|
||||
}
|
||||
view {
|
||||
<section><h2 class="text-lg font-bold">{title}</h2><ol class="mt-5 border-l border-slate-200 pl-6 dark:border-slate-800"><slot /></ol></section>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component TimelineItem { props { title = "Event" date = "" description = "" status = "default" } view { <li class="relative pb-8 last:pb-0"><span class="absolute -left-[1.85rem] top-1 size-3 rounded-full ring-4 ring-white dark:ring-slate-950 {status === 'success' ? 'bg-emerald-500' : status === 'warning' ? 'bg-amber-500' : 'bg-violet-500'}"></span><div class="flex flex-wrap items-baseline justify-between gap-2"><h3 class="font-semibold">{title}</h3><time class="text-xs text-slate-500">{date}</time></div><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></li> } }
|
||||
component TimelineItem {
|
||||
props {
|
||||
title = "Event"
|
||||
date = ""
|
||||
description = ""
|
||||
status = "default"
|
||||
}
|
||||
view {
|
||||
<li class="relative pb-8 last:pb-0"><span class="absolute -left-[1.85rem] top-1 size-3 rounded-full ring-4 ring-white dark:ring-slate-950 {status === 'success' ? 'bg-emerald-500' : status === 'warning' ? 'bg-amber-500' : 'bg-violet-500'}"></span><div class="flex flex-wrap items-baseline justify-between gap-2"><h3 class="font-semibold">{title}</h3><time class="text-xs text-slate-500">{date}</time></div><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></li>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component TimezoneSelector { props { id = "timezone" name = "timezone" label = "Timezone" } view { <Select id="{id}" name="{name}" label="{label}"><option value="UTC">UTC</option><option value="Asia/Kolkata">Asia/Kolkata</option><option value="America/New_York">America/New_York</option><option value="Europe/London">Europe/London</option></Select> } }
|
||||
component TimezoneSelector {
|
||||
props {
|
||||
id = "timezone"
|
||||
name = "timezone"
|
||||
label = "Timezone"
|
||||
}
|
||||
view {
|
||||
<Select id="{id}" name="{name}" label="{label}"><option value="UTC">UTC</option><option value="Asia/Kolkata">Asia/Kolkata</option><option value="America/New_York">America/New_York</option><option value="Europe/London">Europe/London</option></Select>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,12 @@
|
||||
component Toast { props { title = "Saved" description = "" variant = "success" duration = 5000 } state visible = true view { <div data-show="visible" role="status" aria-live="polite" class="pointer-events-auto flex w-full max-w-sm items-start gap-3 rounded-2xl border bg-white p-4 shadow-2xl dark:border-slate-800 dark:bg-slate-900"><span class="mt-0.5 flex size-8 items-center justify-center rounded-full {variant === 'success' ? 'bg-emerald-100 text-emerald-700' : variant === 'danger' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}">{variant === 'success' ? '✓' : variant === 'danger' ? '!' : 'i'}</span><div class="min-w-0 flex-1"><p class="font-semibold">{title}</p><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="Close" @click="visible = false" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100">×</button></div> } }
|
||||
component Toast {
|
||||
props {
|
||||
title = "Saved"
|
||||
description = ""
|
||||
variant = "success"
|
||||
duration = 5000
|
||||
}
|
||||
state visible = true
|
||||
view {
|
||||
<div data-show="visible" role="status" aria-live="polite" class="pointer-events-auto flex w-full max-w-sm items-start gap-3 rounded-2xl border bg-white p-4 shadow-2xl dark:border-slate-800 dark:bg-slate-900"><span class="mt-0.5 flex size-8 items-center justify-center rounded-full {variant === 'success' ? 'bg-emerald-100 text-emerald-700' : variant === 'danger' ? 'bg-red-100 text-red-700' : 'bg-blue-100 text-blue-700'}">{variant === 'success' ? '✓' : variant === 'danger' ? '!' : 'i'}</span><div class="min-w-0 flex-1"><p class="font-semibold">{title}</p><p data-show="description" class="mt-1 text-sm text-slate-500">{description}</p></div><button type="button" aria-label="Close" @click="visible = false" class="rounded-lg p-1 text-slate-400 hover:bg-slate-100">×</button></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
component UrlInput {
|
||||
props { id = "" name = "" label = "Url" value = "" placeholder = "" help = "" error = "" required = false disabled = false readonly = false autocomplete = "" }
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Url"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
readonly = false
|
||||
autocomplete = ""
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2">
|
||||
<label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label>
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component VisuallyHidden { props { className = "" } view { <div class="sr-only {className}"><slot /></div> } }
|
||||
component VisuallyHidden {
|
||||
props {
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="sr-only {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Avatar { props { src = "" alt = "" initials = "WR" size = "md" } view { <span class="inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 font-semibold text-white {size === 'sm' ? 'size-8 text-xs' : size === 'lg' ? 'size-14 text-base' : 'size-10 text-sm'}"><img data-show="src" src="{src}" alt="{alt}" class="h-full w-full object-cover" /><span data-show="!src" aria-hidden="true">{initials}</span></span> } }
|
||||
component Avatar {
|
||||
props {
|
||||
src = ""
|
||||
alt = ""
|
||||
initials = "WR"
|
||||
size = "md"
|
||||
}
|
||||
view {
|
||||
<span class="inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 font-semibold text-white {size === 'sm' ? 'size-8 text-xs' : size === 'lg' ? 'size-14 text-base' : 'size-10 text-sm'}"><img data-show="src" src="{src}" alt="{alt}" class="h-full w-full object-cover" /><span data-show="!src" aria-hidden="true">{initials}</span></span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Badge { props { label = "Badge" variant = "neutral" } view { <span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ring-1 ring-inset {variant === 'success' ? 'bg-emerald-50 text-emerald-700 ring-emerald-600/20 dark:bg-emerald-950/40 dark:text-emerald-300' : variant === 'warning' ? 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-950/40 dark:text-amber-300' : variant === 'danger' ? 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-950/40 dark:text-red-300' : variant === 'brand' ? 'bg-violet-50 text-violet-700 ring-violet-600/20 dark:bg-violet-950/40 dark:text-violet-300' : 'bg-slate-100 text-slate-700 ring-slate-600/10 dark:bg-slate-800 dark:text-slate-300'}">{label}</span> } }
|
||||
component Badge {
|
||||
props {
|
||||
label = "Badge"
|
||||
variant = "neutral"
|
||||
}
|
||||
view {
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ring-1 ring-inset {variant === 'success' ? 'bg-emerald-50 text-emerald-700 ring-emerald-600/20 dark:bg-emerald-950/40 dark:text-emerald-300' : variant === 'warning' ? 'bg-amber-50 text-amber-700 ring-amber-600/20 dark:bg-amber-950/40 dark:text-amber-300' : variant === 'danger' ? 'bg-red-50 text-red-700 ring-red-600/20 dark:bg-red-950/40 dark:text-red-300' : variant === 'brand' ? 'bg-violet-50 text-violet-700 ring-violet-600/20 dark:bg-violet-950/40 dark:text-violet-300' : 'bg-slate-100 text-slate-700 ring-slate-600/10 dark:bg-slate-800 dark:text-slate-300'}">{label}</span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
component Button {
|
||||
props { label = "Button" type = "button" variant = "primary" size = "md" disabled = false loading = false icon = "" className = "" }
|
||||
props {
|
||||
label = "Button"
|
||||
type = "button"
|
||||
variant = "primary"
|
||||
size = "md"
|
||||
disabled = false
|
||||
loading = false
|
||||
icon = ""
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<button type="{type}" disabled="{disabled || loading}" aria-busy="{loading}" class="inline-flex min-h-11 items-center justify-center gap-2 rounded-xl font-semibold transition-all duration-200 ease-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 active:scale-[0.98] {size === 'sm' ? 'px-3 py-2 text-sm' : size === 'lg' ? 'px-6 py-3.5 text-base' : 'px-4 py-2.5 text-sm'} {variant === 'secondary' ? 'border border-slate-300 bg-white text-slate-900 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-white dark:hover:bg-slate-800' : variant === 'ghost' ? 'text-slate-700 hover:bg-slate-100 dark:text-slate-200 dark:hover:bg-slate-800' : variant === 'danger' ? 'bg-red-600 text-white hover:bg-red-700' : 'bg-violet-600 text-white shadow-sm hover:-translate-y-0.5 hover:bg-violet-700 hover:shadow-md'} {className}">
|
||||
<span data-show="loading" class="size-4 animate-spin rounded-full border-2 border-current border-r-transparent"></span>
|
||||
|
||||
@@ -1 +1,11 @@
|
||||
component Card { props { variant = "default" padding = "md" interactive = false className = "" } view { <article class="rounded-2xl border border-slate-200 bg-white text-slate-950 shadow-sm transition duration-300 dark:border-slate-800 dark:bg-slate-900 dark:text-white {padding === 'sm' ? 'p-4' : padding === 'lg' ? 'p-8' : 'p-6'} {variant === 'glass' ? 'bg-white/75 backdrop-blur-xl dark:bg-slate-900/70' : variant === 'elevated' ? 'shadow-xl shadow-slate-950/5' : ''} {interactive ? 'hover:-translate-y-1 hover:shadow-xl' : ''} {className}"><slot /></article> } }
|
||||
component Card {
|
||||
props {
|
||||
variant = "default"
|
||||
padding = "md"
|
||||
interactive = false
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<article class="rounded-2xl border border-slate-200 bg-white text-slate-950 shadow-sm transition duration-300 dark:border-slate-800 dark:bg-slate-900 dark:text-white {padding === 'sm' ? 'p-4' : padding === 'lg' ? 'p-8' : 'p-6'} {variant === 'glass' ? 'bg-white/75 backdrop-blur-xl dark:bg-slate-900/70' : variant === 'elevated' ? 'shadow-xl shadow-slate-950/5' : ''} {interactive ? 'hover:-translate-y-1 hover:shadow-xl' : ''} {className}"><slot /></article>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,13 @@
|
||||
component Checkbox { props { id = "" name = "" label = "Checkbox" description = "" checked = false disabled = false } view { <label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl p-2 transition hover:bg-slate-50 dark:hover:bg-slate-900"><input id="{id}" name="{name}" type="checkbox" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 rounded border-slate-300 text-violet-600 focus:ring-violet-500 dark:border-slate-700 dark:bg-slate-950" /><span><span class="block text-sm font-semibold text-slate-900 dark:text-white">{label}</span><span data-show="description" class="mt-0.5 block text-sm text-slate-500 dark:text-slate-400">{description}</span></span></label> } }
|
||||
component Checkbox {
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Checkbox"
|
||||
description = ""
|
||||
checked = false
|
||||
disabled = false
|
||||
}
|
||||
view {
|
||||
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl p-2 transition hover:bg-slate-50 dark:hover:bg-slate-900"><input id="{id}" name="{name}" type="checkbox" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 rounded border-slate-300 text-violet-600 focus:ring-violet-500 dark:border-slate-700 dark:bg-slate-950" /><span><span class="block text-sm font-semibold text-slate-900 dark:text-white">{label}</span><span data-show="description" class="mt-0.5 block text-sm text-slate-500 dark:text-slate-400">{description}</span></span></label>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
component Container {
|
||||
props { size = "default" className = "" }
|
||||
props {
|
||||
size = "default"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="mx-auto w-full px-4 sm:px-6 lg:px-8 {size === 'narrow' ? 'max-w-3xl' : size === 'wide' ? 'max-w-screen-2xl' : 'max-w-7xl'} {className}"><slot /></div>
|
||||
}
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
component Divider { props { className = "" } view { <div class="h-px w-full bg-slate-200 dark:bg-slate-800 {className}"><slot /></div> } }
|
||||
component Divider {
|
||||
props {
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="h-px w-full bg-slate-200 dark:bg-slate-800 {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Grid { props { gap = "4" className = "" } view { <div class="grid gap-{gap} {className}"><slot /></div> } }
|
||||
component Grid {
|
||||
props {
|
||||
gap = "4"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="grid gap-{gap} {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component Radio { props { id = "" name = "choice" value = "" label = "Option" description = "" checked = false disabled = false } view { <label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl border border-slate-200 p-3 transition hover:border-violet-400 dark:border-slate-800"><input id="{id}" name="{name}" type="radio" value="{value}" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 border-slate-300 text-violet-600 focus:ring-violet-500" /><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span></label> } }
|
||||
component Radio {
|
||||
props {
|
||||
id = ""
|
||||
name = "choice"
|
||||
value = ""
|
||||
label = "Option"
|
||||
description = ""
|
||||
checked = false
|
||||
disabled = false
|
||||
}
|
||||
view {
|
||||
<label for="{id}" class="flex min-h-11 cursor-pointer items-start gap-3 rounded-xl border border-slate-200 p-3 transition hover:border-violet-400 dark:border-slate-800"><input id="{id}" name="{name}" type="radio" value="{value}" checked="{checked}" disabled="{disabled}" class="mt-0.5 size-5 border-slate-300 text-violet-600 focus:ring-violet-500" /><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span></label>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component Select { props { id = "" name = "" label = "Select" help = "" error = "" required = false disabled = false } view { <div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><select id="{id}" name="{name}" required="{required}" disabled="{disabled}" class="min-h-11 w-full rounded-xl border border-slate-300 bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950 dark:text-white"><slot /></select><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div> } }
|
||||
component Select {
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Select"
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><select id="{id}" name="{name}" required="{required}" disabled="{disabled}" class="min-h-11 w-full rounded-xl border border-slate-300 bg-white px-3.5 py-2.5 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:border-slate-700 dark:bg-slate-950 dark:text-white"><slot /></select><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
component Skeleton { props { height = "4" rounded = "lg" className = "" } view { <div aria-hidden="true" class="animate-pulse bg-slate-200 dark:bg-slate-800 h-{height} rounded-{rounded} {className}"></div> } }
|
||||
component Skeleton {
|
||||
props {
|
||||
height = "4"
|
||||
rounded = "lg"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div aria-hidden="true" class="animate-pulse bg-slate-200 dark:bg-slate-800 h-{height} rounded-{rounded} {className}"></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Spinner { props { label = "Loading" size = "md" } view { <span role="status" aria-label="{label}" class="inline-block animate-spin rounded-full border-2 border-slate-300 border-r-violet-600 {size === 'sm' ? 'size-4' : size === 'lg' ? 'size-10' : 'size-6'}"></span> } }
|
||||
component Spinner {
|
||||
props {
|
||||
label = "Loading"
|
||||
size = "md"
|
||||
}
|
||||
view {
|
||||
<span role="status" aria-label="{label}" class="inline-block animate-spin rounded-full border-2 border-slate-300 border-r-violet-600 {size === 'sm' ? 'size-4' : size === 'lg' ? 'size-10' : 'size-6'}"></span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Stack { props { gap = "4" className = "" } view { <div class="flex flex-col gap-{gap} {className}"><slot /></div> } }
|
||||
component Stack {
|
||||
props {
|
||||
gap = "4"
|
||||
className = ""
|
||||
}
|
||||
view {
|
||||
<div class="flex flex-col gap-{gap} {className}"><slot /></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,14 @@
|
||||
component Switch { props { id = "" name = "" label = "Switch" description = "" checked = false disabled = false } state on = checked view { <button id="{id}" type="button" role="switch" aria-checked="{on}" disabled="{disabled}" @click="on = !on" class="flex w-full items-center justify-between gap-4 rounded-xl p-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50"><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span><span class="relative inline-flex h-6 w-11 shrink-0 rounded-full transition {on ? 'bg-violet-600' : 'bg-slate-300 dark:bg-slate-700'}"><span class="absolute top-0.5 size-5 rounded-full bg-white shadow transition-transform {on ? 'translate-x-5' : 'translate-x-0.5'}"></span></span><input type="hidden" name="{name}" value="{on ? 'true' : 'false'}" /></button> } }
|
||||
component Switch {
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Switch"
|
||||
description = ""
|
||||
checked = false
|
||||
disabled = false
|
||||
}
|
||||
state on = checked
|
||||
view {
|
||||
<button id="{id}" type="button" role="switch" aria-checked="{on}" disabled="{disabled}" @click="on = !on" class="flex w-full items-center justify-between gap-4 rounded-xl p-2 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 disabled:opacity-50"><span><span class="block text-sm font-semibold">{label}</span><span data-show="description" class="text-sm text-slate-500">{description}</span></span><span class="relative inline-flex h-6 w-11 shrink-0 rounded-full transition {on ? 'bg-violet-600' : 'bg-slate-300 dark:bg-slate-700'}"><span class="absolute top-0.5 size-5 rounded-full bg-white shadow transition-transform {on ? 'translate-x-5' : 'translate-x-0.5'}"></span></span><input type="hidden" name="{name}" value="{on ? 'true' : 'false'}" /></button>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Table { props { caption = "Data table" responsive = true } view { <div class="w-full overflow-x-auto rounded-2xl border border-slate-200 dark:border-slate-800"><table class="w-full min-w-[640px] border-collapse text-left text-sm"><caption class="sr-only">{caption}</caption><slot /></table></div> } }
|
||||
component Table {
|
||||
props {
|
||||
caption = "Data table"
|
||||
responsive = true
|
||||
}
|
||||
view {
|
||||
<div class="w-full overflow-x-auto rounded-2xl border border-slate-200 dark:border-slate-800"><table class="w-full min-w-[640px] border-collapse text-left text-sm"><caption class="sr-only">{caption}</caption><slot /></table></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,17 @@
|
||||
component Textarea { props { id = "" name = "" label = "Message" value = "" placeholder = "" rows = 5 help = "" error = "" required = false disabled = false } view { <div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><textarea id="{id}" name="{name}" rows="{rows}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" aria-invalid="{!!error}" class="w-full resize-y rounded-xl border bg-white px-3.5 py-3 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:bg-slate-950 dark:text-white {error ? 'border-red-500' : 'border-slate-300 dark:border-slate-700'}">{value}</textarea><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div> } }
|
||||
component Textarea {
|
||||
props {
|
||||
id = ""
|
||||
name = ""
|
||||
label = "Message"
|
||||
value = ""
|
||||
placeholder = ""
|
||||
rows = 5
|
||||
help = ""
|
||||
error = ""
|
||||
required = false
|
||||
disabled = false
|
||||
}
|
||||
view {
|
||||
<div class="space-y-2"><label for="{id}" class="block text-sm font-semibold text-slate-800 dark:text-slate-200">{label}<span data-show="required" class="ml-1 text-red-500">*</span></label><textarea id="{id}" name="{name}" rows="{rows}" placeholder="{placeholder}" required="{required}" disabled="{disabled}" aria-invalid="{!!error}" class="w-full resize-y rounded-xl border bg-white px-3.5 py-3 text-slate-950 shadow-sm outline-none transition focus:border-violet-500 focus:ring-4 focus:ring-violet-500/10 dark:bg-slate-950 dark:text-white {error ? 'border-red-500' : 'border-slate-300 dark:border-slate-700'}">{value}</textarea><p data-show="help && !error" class="text-sm text-slate-500">{help}</p><p data-show="error" role="alert" class="text-sm font-medium text-red-600">{error}</p></div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
component Tooltip { props { text = "Helpful information" position = "top" } view { <span class="group relative inline-flex"><slot /><span role="tooltip" class="pointer-events-none absolute z-40 w-max max-w-xs rounded-lg bg-slate-950 px-2.5 py-1.5 text-xs font-medium text-white opacity-0 shadow-lg transition duration-200 group-hover:opacity-100 group-focus-within:opacity-100 {position === 'bottom' ? 'left-1/2 top-full mt-2 -translate-x-1/2' : 'bottom-full left-1/2 mb-2 -translate-x-1/2'}">{text}</span></span> } }
|
||||
component Tooltip {
|
||||
props {
|
||||
text = "Helpful information"
|
||||
position = "top"
|
||||
}
|
||||
view {
|
||||
<span class="group relative inline-flex"><slot /><span role="tooltip" class="pointer-events-none absolute z-40 w-max max-w-xs rounded-lg bg-slate-950 px-2.5 py-1.5 text-xs font-medium text-white opacity-0 shadow-lg transition duration-200 group-hover:opacity-100 group-focus-within:opacity-100 {position === 'bottom' ? 'left-1/2 top-full mt-2 -translate-x-1/2' : 'bottom-full left-1/2 mb-2 -translate-x-1/2'}">{text}</span></span>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -23,7 +23,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.2.50"
|
||||
"@wrnexus/core": "^0.2.51"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.2.50",
|
||||
"version": "0.2.51",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -666,8 +666,8 @@ runs at dev-serve time and at `wrnexus build`, and returns the final CSS:
|
||||
const config: AppConfig = {
|
||||
styles: {
|
||||
entry: "app/styles/global.css",
|
||||
process: async ({ entryPath }) =>
|
||||
await Bun.$`bunx @tailwindcss/cli -i ${entryPath} --minify`.text(),
|
||||
process: async ({ entryPath, appRoot }) =>
|
||||
await Bun.$.cwd(appRoot)`bunx @tailwindcss/cli -i ${entryPath} --minify`.text(),
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
+2
-2
@@ -532,10 +532,10 @@ Add a CSS framework two ways:
|
||||
```ts
|
||||
styles: {
|
||||
entry: "app/styles/global.css",
|
||||
process: async ({ entryPath, mode }) => {
|
||||
process: async ({ entryPath, appRoot, mode }) => {
|
||||
const args = ["@tailwindcss/cli", "-i", entryPath!];
|
||||
if (mode === "production") args.push("--minify");
|
||||
return await Bun.$`bunx ${args}`.text();
|
||||
return await Bun.$.cwd(appRoot)`bunx ${args}`.text();
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user