release: WRNexusJS 0.2.54
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ai",
|
"name": "@wrnexus/ai",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/authz",
|
"name": "@wrnexus/authz",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
+11
-11
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -28,16 +28,16 @@
|
|||||||
"wrnexus": "./dist/index.js"
|
"wrnexus": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53",
|
"@wrnexus/core": "^0.2.54",
|
||||||
"@wrnexus/router": "^0.2.53",
|
"@wrnexus/router": "^0.2.54",
|
||||||
"@wrnexus/csr": "^0.2.53",
|
"@wrnexus/csr": "^0.2.54",
|
||||||
"@wrnexus/compiler": "^0.2.53",
|
"@wrnexus/compiler": "^0.2.54",
|
||||||
"@wrnexus/styles": "^0.2.53",
|
"@wrnexus/styles": "^0.2.54",
|
||||||
"@wrnexus/dev-server": "^0.2.53",
|
"@wrnexus/dev-server": "^0.2.54",
|
||||||
"@wrnexus/ui": "^0.2.53",
|
"@wrnexus/ui": "^0.2.54",
|
||||||
"@wrnexus/validation": "^0.2.53",
|
"@wrnexus/validation": "^0.2.54",
|
||||||
"@wrnexus/i18n": "^0.2.53",
|
"@wrnexus/i18n": "^0.2.54",
|
||||||
"@wrnexus/db": "^0.2.53"
|
"@wrnexus/db": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/core",
|
"name": "@wrnexus/core",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/csr",
|
"name": "@wrnexus/csr",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/db — part of the WrNexus framework.",
|
"description": "@wrnexus/db — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -25,18 +25,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53",
|
"@wrnexus/core": "^0.2.54",
|
||||||
"@wrnexus/router": "^0.2.53",
|
"@wrnexus/router": "^0.2.54",
|
||||||
"@wrnexus/ssr": "^0.2.53",
|
"@wrnexus/ssr": "^0.2.54",
|
||||||
"@wrnexus/csr": "^0.2.53",
|
"@wrnexus/csr": "^0.2.54",
|
||||||
"@wrnexus/compiler": "^0.2.53",
|
"@wrnexus/compiler": "^0.2.54",
|
||||||
"@wrnexus/styles": "^0.2.53",
|
"@wrnexus/styles": "^0.2.54",
|
||||||
"@wrnexus/ui": "^0.2.53",
|
"@wrnexus/ui": "^0.2.54",
|
||||||
"@wrnexus/validation": "^0.2.53",
|
"@wrnexus/validation": "^0.2.54",
|
||||||
"@wrnexus/i18n": "^0.2.53",
|
"@wrnexus/i18n": "^0.2.54",
|
||||||
"@wrnexus/db": "^0.2.53",
|
"@wrnexus/db": "^0.2.54",
|
||||||
"@wrnexus/pubsub": "^0.2.53",
|
"@wrnexus/pubsub": "^0.2.54",
|
||||||
"@wrnexus/uploader": "^0.2.53"
|
"@wrnexus/uploader": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/encryption",
|
"name": "@wrnexus/encryption",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
"description": "@wrnexus/encryption — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/helpers",
|
"name": "@wrnexus/helpers",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/i18n",
|
"name": "@wrnexus/i18n",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
"description": "@wrnexus/i18n — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/jwt",
|
"name": "@wrnexus/jwt",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
"description": "@wrnexus/jwt — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/mobile",
|
"name": "@wrnexus/mobile",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/native": "^0.2.53"
|
"@wrnexus/native": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/native",
|
"name": "@wrnexus/native",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/oauth",
|
"name": "@wrnexus/oauth",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/pubsub",
|
"name": "@wrnexus/pubsub",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/queue",
|
"name": "@wrnexus/queue",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/reactive",
|
"name": "@wrnexus/reactive",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/router",
|
"name": "@wrnexus/router",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/compiler": "^0.2.53",
|
"@wrnexus/compiler": "^0.2.54",
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ssr",
|
"name": "@wrnexus/ssr",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/styles",
|
"name": "@wrnexus/styles",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/uploader": "^0.2.53"
|
"@wrnexus/uploader": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/test",
|
"name": "@wrnexus/test",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/tracking",
|
"name": "@wrnexus/tracking",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -67,6 +67,17 @@ body from either a named prop or the default slot.
|
|||||||
Also shipped: `select`, `radio`, `switch`, `progress`, `tag`, `skeleton`,
|
Also shipped: `select`, `radio`, `switch`, `progress`, `tag`, `skeleton`,
|
||||||
`tooltip`, `table`, `FAQAccordion`, `AnnouncementBar`, and `BackToTop`.
|
`tooltip`, `table`, `FAQAccordion`, `AnnouncementBar`, and `BackToTop`.
|
||||||
|
|
||||||
|
The PDF-defined minimum release and essential build-first set also includes
|
||||||
|
typed typography, form primitives, loading actions, combobox and multi-select,
|
||||||
|
time/date-time and recurring schedule controls, confirmation dialogs, data
|
||||||
|
tables, filters, desktop/mobile navigation, mega menus, marketing/product/legal
|
||||||
|
page shells, product and metric cards, FAQ composition, pricing comparison,
|
||||||
|
SDK tabs, legal navigation, and cookie preferences.
|
||||||
|
|
||||||
|
`Seo` and `StructuredData` remain framework/page concerns rather than body
|
||||||
|
components: use the native page `seo { ... }` block and document-head APIs so
|
||||||
|
metadata is emitted in `<head>` instead of invalid component markup.
|
||||||
|
|
||||||
The authoritative, always-current list is `uiComponentNames()` (below), which reads
|
The authoritative, always-current list is `uiComponentNames()` (below), which reads
|
||||||
the component directory at runtime.
|
the component directory at runtime.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component Combobox {
|
||||||
|
props {
|
||||||
|
id = "combobox"
|
||||||
|
name = ""
|
||||||
|
label = "Choose an option"
|
||||||
|
placeholder = "Search options"
|
||||||
|
options = []
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><input id="{id}" name="{name}" list="{id}-options" placeholder="{placeholder}" required="{required}" disabled="{disabled}" role="combobox" aria-autocomplete="list" class="wire-input" /><datalist id="{id}-options">{#each options as option}<option value="{option.value}">{option.label}</option>{/each}</datalist></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component ConfirmationDialog {
|
||||||
|
props {
|
||||||
|
title = "Confirm action"
|
||||||
|
description = "Are you sure you want to continue?"
|
||||||
|
confirmLabel = "Confirm"
|
||||||
|
cancelLabel = "Cancel"
|
||||||
|
danger = false
|
||||||
|
open = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state visible = open
|
||||||
|
view {
|
||||||
|
<div data-show="visible" class="wire-overlay {class}" role="presentation"><section role="alertdialog" aria-modal="true" aria-labelledby="confirmation-title" aria-describedby="confirmation-description" class="wire-dialog"><h2 id="confirmation-title" class="wire-dialog__title">{title}</h2><p id="confirmation-description" class="wire-dialog__description">{description}</p><div class="wire-dialog__actions"><button type="button" class="wire-btn wire-btn--ghost" @click="visible = false">{cancelLabel}</button><button type="button" class="wire-btn {danger ? 'wire-btn--danger' : 'wire-btn--primary'}"><slot name="confirm">{confirmLabel}</slot></button></div></section></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
component CookiePreferencesDialog {
|
||||||
|
props {
|
||||||
|
title = "Cookie preferences"
|
||||||
|
description = "Choose which optional cookies you allow. Essential cookies are always enabled."
|
||||||
|
saveLabel = "Save preferences"
|
||||||
|
acceptLabel = "Accept all"
|
||||||
|
rejectLabel = "Reject optional"
|
||||||
|
open = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state visible = open
|
||||||
|
state analytics = false
|
||||||
|
state marketing = false
|
||||||
|
view {
|
||||||
|
<div data-show="visible" class="wire-overlay {class}" role="presentation"><section role="dialog" aria-modal="true" aria-labelledby="cookie-preferences-title" class="wire-dialog wire-cookie-dialog"><h2 id="cookie-preferences-title" class="wire-dialog__title">{title}</h2><p class="wire-dialog__description">{description}</p><div class="wire-cookie-list"><label class="wire-cookie-option"><span><strong>Essential</strong><small>Required for security and core functionality.</small></span><input type="checkbox" checked disabled /></label><label class="wire-cookie-option"><span><strong>Analytics</strong><small>Helps improve product performance.</small></span><input type="checkbox" checked="{analytics}" @change="analytics = event.target.checked" /></label><label class="wire-cookie-option"><span><strong>Marketing</strong><small>Supports relevant campaigns and measurement.</small></span><input type="checkbox" checked="{marketing}" @change="marketing = event.target.checked" /></label></div><div class="wire-dialog__actions wire-dialog__actions--wrap"><button type="button" class="wire-btn wire-btn--ghost" @click="analytics = false; marketing = false">{rejectLabel}</button><button type="button" class="wire-btn wire-btn--default" @click="analytics = true; marketing = true">{acceptLabel}</button><button type="button" class="wire-btn wire-btn--primary" @click="visible = false">{saveLabel}</button></div></section></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
component DataTable {
|
||||||
|
props {
|
||||||
|
caption = "Data table"
|
||||||
|
columns = []
|
||||||
|
rows = []
|
||||||
|
emptyMessage = "No data available."
|
||||||
|
striped = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-table-wrap {class}" tabindex="0" role="region" aria-label="{caption}"><table class="wire-table {striped ? 'wire-table--striped' : ''}"><caption class="wire-visually-hidden">{caption}</caption><thead><tr>{#each columns as column}<th scope="col">{column.label}</th>{/each}</tr></thead><tbody>{#each rows as row}<tr>{#each columns as column}<td>{row[column.key]}</td>{/each}</tr>{:empty}<tr><td colspan="{columns.length}">{emptyMessage}</td></tr>{/each}</tbody></table></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
component DateTimePicker {
|
||||||
|
props {
|
||||||
|
id = "datetime-picker"
|
||||||
|
name = "datetime"
|
||||||
|
label = "Date and time"
|
||||||
|
value = ""
|
||||||
|
min = ""
|
||||||
|
max = ""
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><input id="{id}" name="{name}" type="datetime-local" value="{value}" min="{min}" max="{max}" required="{required}" disabled="{disabled}" class="wire-input" /></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component DesktopNavigation {
|
||||||
|
props {
|
||||||
|
label = "Primary navigation"
|
||||||
|
items = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <nav aria-label="{label}" class="wire-desktop-nav {class}"><ul>{#each items as item}<li><a href="{item.href}" aria-current="{item.current ? 'page' : 'false'}">{item.label}</a></li>{/each}</ul></nav> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component FAQ {
|
||||||
|
props {
|
||||||
|
eyebrow = ""
|
||||||
|
title = "Frequently asked questions"
|
||||||
|
description = ""
|
||||||
|
items = []
|
||||||
|
centered = true
|
||||||
|
allowMultiple = false
|
||||||
|
defaultOpenIndex = 0
|
||||||
|
}
|
||||||
|
view { <FAQAccordion eyebrow="{eyebrow}" title="{title}" description="{description}" items="{items}" centered="{centered}" allowMultiple="{allowMultiple}" defaultOpenIndex="{defaultOpenIndex}" /> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
component FilterBar {
|
||||||
|
props {
|
||||||
|
label = "Filters"
|
||||||
|
clearLabel = "Clear filters"
|
||||||
|
showClear = true
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <section aria-label="{label}" class="wire-filter-bar {class}"><div class="wire-filter-bar__controls"><slot /></div><button data-show="showClear" type="reset" class="wire-btn wire-btn--ghost wire-btn--sm">{clearLabel}</button></section> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component Form {
|
||||||
|
props {
|
||||||
|
action = ""
|
||||||
|
method = "post"
|
||||||
|
name = ""
|
||||||
|
loading = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<form action="{action}" method="{method}" name="{name}" aria-busy="{loading}" class="wire-form {class}"><slot /></form>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component FormError {
|
||||||
|
props {
|
||||||
|
id = ""
|
||||||
|
message = ""
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <p id="{id}" role="alert" aria-live="polite" class="wire-field-message wire-field-message--error {class}">{message}<slot /></p> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component FormHelpText {
|
||||||
|
props {
|
||||||
|
id = ""
|
||||||
|
text = ""
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <p id="{id}" class="wire-field-message wire-field-message--help {class}">{text}<slot /></p> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component FormLabel {
|
||||||
|
props {
|
||||||
|
for = ""
|
||||||
|
label = "Label"
|
||||||
|
required = false
|
||||||
|
optional = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<label for="{for}" class="wire-label {class}">{label}<span data-show="required" class="wire-required" aria-hidden="true"> *</span><span data-show="optional && !required" class="wire-optional"> (optional)</span></label>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
component LegalPageShell {
|
||||||
|
props {
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-page-shell wire-page-shell--legal {class}"><aside class="wire-page-shell__aside"><slot name="navigation" /></aside><main class="wire-page-shell__main"><slot /></main></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component LegalTableOfContents {
|
||||||
|
props {
|
||||||
|
title = "On this page"
|
||||||
|
items = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <nav aria-label="{title}" class="wire-legal-toc {class}"><h2>{title}</h2><ol>{#each items as item}<li><a href="{item.href}">{item.label}</a></li>{/each}</ol></nav> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
component LoadingButton {
|
||||||
|
props {
|
||||||
|
label = "Continue"
|
||||||
|
loadingLabel = "Loading…"
|
||||||
|
loading = false
|
||||||
|
disabled = false
|
||||||
|
type = "button"
|
||||||
|
variant = "primary"
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<button type="{type}" disabled="{disabled || loading}" aria-disabled="{disabled || loading}" aria-busy="{loading}" class="wire-btn wire-btn--{variant} wire-loading-button {class}"><span data-show="loading" class="wire-spinner wire-spinner--inline" aria-hidden="true"></span><span>{loading ? loadingLabel : label}</span></button>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
component MarketingPageShell {
|
||||||
|
props {
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-page-shell wire-page-shell--marketing {class}"><slot /></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
component MegaMenu {
|
||||||
|
props {
|
||||||
|
label = "Explore"
|
||||||
|
sections = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state open = false
|
||||||
|
view {
|
||||||
|
<div class="wire-mega-menu {class}"><button type="button" class="wire-mega-menu__trigger" aria-expanded="{open}" @click="open = !open">{label}<span aria-hidden="true">⌄</span></button><div data-show="open" class="wire-mega-menu__panel">{#each sections as section}<section><h3>{section.title}</h3><ul>{#each section.items as item}<li><a href="{item.href}">{item.label}</a></li>{/each}</ul></section>{/each}</div></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component MetricGrid {
|
||||||
|
props {
|
||||||
|
label = "Key metrics"
|
||||||
|
metrics = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <section aria-label="{label}" class="wire-metric-grid {class}">{#each metrics as metric}<article class="wire-metric"><p class="wire-metric__value">{metric.value}</p><p class="wire-metric__label">{metric.label}</p><p data-show="metric.description" class="wire-metric__description">{metric.description}</p></article>{/each}</section> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component MobileNavigation {
|
||||||
|
props {
|
||||||
|
label = "Menu"
|
||||||
|
closeLabel = "Close menu"
|
||||||
|
items = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state open = false
|
||||||
|
view {
|
||||||
|
<div class="wire-mobile-nav {class}"><button type="button" class="wire-btn wire-btn--ghost" aria-expanded="{open}" aria-controls="wire-mobile-navigation" @click="open = !open">{open ? closeLabel : label}</button><nav id="wire-mobile-navigation" data-show="open" aria-label="Mobile navigation" class="wire-mobile-nav__panel"><ul>{#each items as item}<li><a href="{item.href}">{item.label}</a></li>{/each}</ul></nav></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component MultiSelect {
|
||||||
|
props {
|
||||||
|
id = "multi-select"
|
||||||
|
name = ""
|
||||||
|
label = "Choose options"
|
||||||
|
options = []
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
size = 5
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><select id="{id}" name="{name}" multiple required="{required}" disabled="{disabled}" size="{size}" class="wire-input wire-multiselect">{#each options as option}<option value="{option.value}" selected="{option.selected}">{option.label}</option>{/each}</select></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
component PricingComparisonTable {
|
||||||
|
props {
|
||||||
|
caption = "Plan comparison"
|
||||||
|
plans = []
|
||||||
|
features = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-table-wrap {class}" tabindex="0" role="region" aria-label="{caption}"><table class="wire-table wire-comparison-table"><caption class="wire-visually-hidden">{caption}</caption><thead><tr><th scope="col">Feature</th>{#each plans as plan}<th scope="col">{plan.name}</th>{/each}</tr></thead><tbody>{#each features as feature}<tr><th scope="row">{feature.label}</th>{#each feature.values as value}<td>{value}</td>{/each}</tr>{/each}</tbody></table></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component ProductCard {
|
||||||
|
props {
|
||||||
|
eyebrow = "Product"
|
||||||
|
title = "Product name"
|
||||||
|
description = ""
|
||||||
|
href = "#"
|
||||||
|
actionLabel = "Learn more"
|
||||||
|
status = ""
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <article class="wire-product-card {class}"><div class="wire-product-card__meta"><span class="wire-eyebrow">{eyebrow}</span><span data-show="status" class="wire-badge">{status}</span></div><h3>{title}</h3><p>{description}</p><a href="{href}" class="wire-product-card__link">{actionLabel}<span aria-hidden="true"> →</span></a></article> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
component ProductPageShell {
|
||||||
|
props {
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-page-shell wire-page-shell--product {class}"><slot /></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component ProductsMegaMenu {
|
||||||
|
props {
|
||||||
|
label = "Products"
|
||||||
|
sections = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <MegaMenu label="{label}" sections="{sections}" class="{class}" /> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component QuietHoursPicker {
|
||||||
|
props {
|
||||||
|
label = "Quiet hours"
|
||||||
|
startName = "quietStart"
|
||||||
|
endName = "quietEnd"
|
||||||
|
start = "22:00"
|
||||||
|
end = "08:00"
|
||||||
|
timezone = "UTC"
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<fieldset class="wire-fieldset {class}" disabled="{disabled}"><legend class="wire-label">{label}</legend><div class="wire-form-row"><label class="wire-field"><span class="wire-label">From</span><input type="time" name="{startName}" value="{start}" class="wire-input" /></label><label class="wire-field"><span class="wire-label">Until</span><input type="time" name="{endName}" value="{end}" class="wire-input" /></label></div><p class="wire-field-message wire-field-message--help">Timezone: {timezone}</p></fieldset>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
component RecurringSchedulePicker {
|
||||||
|
props {
|
||||||
|
id = "recurring-schedule"
|
||||||
|
name = "recurrence"
|
||||||
|
label = "Repeat"
|
||||||
|
value = "none"
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><select id="{id}" name="{name}" disabled="{disabled}" class="wire-input"><option value="none" selected="{value === 'none'}">Does not repeat</option><option value="daily" selected="{value === 'daily'}">Daily</option><option value="weekly" selected="{value === 'weekly'}">Weekly</option><option value="monthly" selected="{value === 'monthly'}">Monthly</option><option value="yearly" selected="{value === 'yearly'}">Yearly</option></select></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
component SDKTabs {
|
||||||
|
props {
|
||||||
|
label = "SDK languages"
|
||||||
|
tabs = []
|
||||||
|
defaultIndex = 0
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state active = defaultIndex
|
||||||
|
view { <section class="wire-sdk-tabs {class}"><div role="tablist" aria-label="{label}" class="wire-tabs">{#each tabs as tab, index}<button type="button" role="tab" aria-selected="{active === index}" class="wire-tab" @click="active = index">{tab.label}</button>{/each}</div>{#each tabs as tab, index}<div role="tabpanel" data-show="active === index" class="wire-sdk-tabs__panel"><pre><code>{tab.code}</code></pre></div>{/each}</section> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component TimePicker {
|
||||||
|
props {
|
||||||
|
id = "time-picker"
|
||||||
|
name = "time"
|
||||||
|
label = "Time"
|
||||||
|
value = ""
|
||||||
|
min = ""
|
||||||
|
max = ""
|
||||||
|
step = 60
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><input id="{id}" name="{name}" type="time" value="{value}" min="{min}" max="{max}" step="{step}" required="{required}" disabled="{disabled}" class="wire-input" /></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
component Typography {
|
||||||
|
props {
|
||||||
|
as = "p"
|
||||||
|
variant = "body"
|
||||||
|
align = "start"
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-type wire-type--{variant} wire-text--{align} {class}"><slot /></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ui",
|
"name": "@wrnexus/ui",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"./ui.css": "./ui.css"
|
"./ui.css": "./ui.css"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
|
|||||||
@@ -537,3 +537,418 @@
|
|||||||
outline: 2px solid rgb(124 58 237);
|
outline: 2px solid rgb(124 58 237);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --- Component-system foundations --------------------------------------- */
|
||||||
|
.wire-type {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
}
|
||||||
|
.wire-type--display {
|
||||||
|
font-size: clamp(2.25rem, 7vw, 4.75rem);
|
||||||
|
line-height: 1.02;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
.wire-type--heading {
|
||||||
|
font-size: clamp(1.75rem, 4vw, 3rem);
|
||||||
|
line-height: 1.1;
|
||||||
|
font-weight: 750;
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
.wire-type--lead {
|
||||||
|
font-size: clamp(1.05rem, 2vw, 1.25rem);
|
||||||
|
line-height: 1.65;
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
}
|
||||||
|
.wire-type--body {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.65;
|
||||||
|
}
|
||||||
|
.wire-type--small {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.wire-text--center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.wire-text--end {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-form {
|
||||||
|
display: grid;
|
||||||
|
gap: 1.25rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.wire-form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.wire-field,
|
||||||
|
.wire-fieldset {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.45rem;
|
||||||
|
min-width: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.wire-label {
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 650;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.wire-required,
|
||||||
|
.wire-field-message--error {
|
||||||
|
color: var(--wire-color-danger);
|
||||||
|
}
|
||||||
|
.wire-optional,
|
||||||
|
.wire-field-message--help {
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.wire-field-message {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.wire-input:disabled,
|
||||||
|
.wire-btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.58;
|
||||||
|
}
|
||||||
|
.wire-input[aria-invalid="true"] {
|
||||||
|
border-color: var(--wire-color-danger);
|
||||||
|
}
|
||||||
|
.wire-multiselect {
|
||||||
|
min-height: 8rem;
|
||||||
|
}
|
||||||
|
.wire-loading-button {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
.wire-spinner--inline {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgb(0 0 0 / 0.55);
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
.wire-dialog {
|
||||||
|
width: min(100%, 32rem);
|
||||||
|
max-height: calc(100dvh - 2rem);
|
||||||
|
overflow: auto;
|
||||||
|
padding: clamp(1.25rem, 4vw, 2rem);
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
background: var(--wire-color-surface);
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius);
|
||||||
|
box-shadow: var(--wire-shadow-1);
|
||||||
|
}
|
||||||
|
.wire-dialog__title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.35rem;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
.wire-dialog__description {
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.wire-dialog__actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
.wire-dialog__actions--wrap {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-filter-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: var(--wire-color-surface);
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius);
|
||||||
|
}
|
||||||
|
.wire-filter-bar__controls {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: end;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.wire-table--striped tbody tr:nth-child(even) {
|
||||||
|
background: var(--wire-color-surface-2);
|
||||||
|
}
|
||||||
|
.wire-comparison-table {
|
||||||
|
min-width: 42rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-desktop-nav > ul,
|
||||||
|
.wire-mobile-nav ul,
|
||||||
|
.wire-mega-menu ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.wire-desktop-nav > ul {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
.wire-desktop-nav a,
|
||||||
|
.wire-mega-menu__trigger {
|
||||||
|
display: inline-flex;
|
||||||
|
min-height: 44px;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-radius: var(--wire-radius-sm);
|
||||||
|
text-decoration: none;
|
||||||
|
font: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.wire-desktop-nav a:hover,
|
||||||
|
.wire-mega-menu__trigger:hover {
|
||||||
|
background: var(--wire-color-surface-2);
|
||||||
|
}
|
||||||
|
.wire-mega-menu {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.wire-mega-menu__panel {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
top: calc(100% + 0.5rem);
|
||||||
|
left: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(10rem, 1fr));
|
||||||
|
gap: 1.25rem;
|
||||||
|
width: min(46rem, calc(100vw - 2rem));
|
||||||
|
padding: 1.25rem;
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
background: var(--wire-color-surface);
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius);
|
||||||
|
box-shadow: var(--wire-shadow-1);
|
||||||
|
}
|
||||||
|
.wire-mega-menu__panel h3 {
|
||||||
|
margin: 0 0 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.wire-mega-menu__panel a,
|
||||||
|
.wire-mobile-nav__panel a {
|
||||||
|
display: flex;
|
||||||
|
min-height: 44px;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.wire-mobile-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.wire-mobile-nav__panel {
|
||||||
|
position: fixed;
|
||||||
|
inset: 4rem 1rem auto;
|
||||||
|
z-index: 100;
|
||||||
|
max-height: calc(100dvh - 5rem);
|
||||||
|
overflow: auto;
|
||||||
|
padding: 1rem;
|
||||||
|
background: var(--wire-color-surface);
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius);
|
||||||
|
box-shadow: var(--wire-shadow-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-page-shell {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
background: var(--wire-color-bg);
|
||||||
|
}
|
||||||
|
.wire-page-shell--marketing,
|
||||||
|
.wire-page-shell--product {
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
.wire-page-shell--legal {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(14rem, 18rem) minmax(0, 1fr);
|
||||||
|
gap: clamp(2rem, 5vw, 5rem);
|
||||||
|
max-width: 80rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
padding: clamp(1rem, 4vw, 3rem);
|
||||||
|
}
|
||||||
|
.wire-page-shell__aside {
|
||||||
|
position: sticky;
|
||||||
|
top: 1rem;
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
.wire-page-shell__main {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 52rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-product-card,
|
||||||
|
.wire-metric {
|
||||||
|
padding: clamp(1rem, 3vw, 1.5rem);
|
||||||
|
color: var(--wire-color-text);
|
||||||
|
background: var(--wire-color-surface);
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius);
|
||||||
|
box-shadow: var(--wire-shadow-1);
|
||||||
|
}
|
||||||
|
.wire-product-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
.wire-product-card__meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
.wire-product-card h3,
|
||||||
|
.wire-product-card p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.wire-product-card p {
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.wire-product-card__link {
|
||||||
|
color: var(--wire-color-primary);
|
||||||
|
font-weight: 650;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.wire-eyebrow {
|
||||||
|
color: var(--wire-color-primary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 750;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.wire-metric-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.wire-metric__value {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
}
|
||||||
|
.wire-metric__label {
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
font-weight: 650;
|
||||||
|
}
|
||||||
|
.wire-metric__description {
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wire-sdk-tabs__panel {
|
||||||
|
overflow-x: auto;
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: 0 0 var(--wire-radius) var(--wire-radius);
|
||||||
|
background: var(--wire-color-surface-2);
|
||||||
|
}
|
||||||
|
.wire-sdk-tabs__panel pre {
|
||||||
|
margin: 0;
|
||||||
|
min-width: max-content;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.wire-legal-toc {
|
||||||
|
padding: 1rem;
|
||||||
|
border-left: 2px solid var(--wire-color-border);
|
||||||
|
}
|
||||||
|
.wire-legal-toc h2 {
|
||||||
|
margin: 0 0 0.75rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.wire-legal-toc ol {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
}
|
||||||
|
.wire-legal-toc a {
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.wire-cookie-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
.wire-cookie-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid var(--wire-color-border);
|
||||||
|
border-radius: var(--wire-radius-sm);
|
||||||
|
}
|
||||||
|
.wire-cookie-option span {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
.wire-cookie-option small {
|
||||||
|
color: var(--wire-color-muted);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.wire-desktop-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.wire-mobile-nav {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.wire-mega-menu__panel {
|
||||||
|
position: static;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.wire-page-shell--legal {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.wire-page-shell__aside {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.wire-form-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.wire-filter-bar {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.wire-filter-bar__controls {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
.wire-dialog__actions > * {
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/uploader",
|
"name": "@wrnexus/uploader",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
"description": "@wrnexus/uploader — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wrnexus/core": "^0.2.53"
|
"@wrnexus/core": "^0.2.54"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/validation",
|
"name": "@wrnexus/validation",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
"description": "@wrnexus/validation — part of the WrNexus framework.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# WRNexus UI component inventory
|
||||||
|
|
||||||
|
This inventory maps the two approved v1.0 PDF checklists to implementation
|
||||||
|
stages. The source documents are **WRNexus Essential Component Inventory v1.0**
|
||||||
|
and **WRNexus Public SaaS Component System Checklist v1.0**.
|
||||||
|
|
||||||
|
## Release boundary
|
||||||
|
|
||||||
|
The current `@wrnexus/ui` release targets the documents' combined **minimum
|
||||||
|
first-release component set** and **essential components to build first**. These
|
||||||
|
are implemented as real components with theme tokens, responsive rules,
|
||||||
|
keyboard-native controls, visible focus, reduced-motion handling, and mobile
|
||||||
|
touch targets—not name-only aliases.
|
||||||
|
|
||||||
|
SEO and structured data are intentionally handled by WRNexus page/head APIs.
|
||||||
|
Rendering them as body components would produce invalid or ineffective HTML.
|
||||||
|
|
||||||
|
## Subsequent staged packages
|
||||||
|
|
||||||
|
The remaining expanded inventory is retained for staged implementation in the
|
||||||
|
dependency order prescribed by the PDFs:
|
||||||
|
|
||||||
|
1. `design-tokens` and `icons`
|
||||||
|
2. `ui-forms`, `ui-calendar`, `ui-feedback`, `ui-overlays`
|
||||||
|
3. `ui-navigation`, `ui-data`, `ui-charts`, `ui-editors`, `ui-media`
|
||||||
|
4. `ui-shell`, `ui-marketing`, `ui-commerce`, `ui-developers`
|
||||||
|
5. `ui-content`, `ui-legal`, `ui-status`
|
||||||
|
|
||||||
|
Each stage must satisfy rendering, keyboard, focus, semantics, disabled/loading
|
||||||
|
states, mobile layout from 320 px, light/dark themes, reduced motion, no
|
||||||
|
horizontal overflow, documentation, and production-build tests before release.
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.2.12
|
||||||
|
|
||||||
|
- Added component-aware prop completions, hover tables, required-prop checks,
|
||||||
|
type mismatch errors, and unknown/invalid-option warnings.
|
||||||
|
|
||||||
## 0.2.11
|
## 0.2.11
|
||||||
|
|
||||||
- Fixed `.wrn` files falling back to Plain Text when a legacy `wire` file
|
- Fixed `.wrn` files falling back to Plain Text when a legacy `wire` file
|
||||||
|
|||||||
@@ -94,6 +94,13 @@ realtime chat {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Put `// @required` immediately above a prop when callers must provide it while
|
||||||
|
retaining a typed runtime fallback. `propName = undefined` is also treated as
|
||||||
|
required when no fallback is appropriate. The editor shows required props first
|
||||||
|
in completion lists and reports an error when a component mount omits one. Prop
|
||||||
|
types are inferred from defaults, and string options used in component comparisons
|
||||||
|
are offered as completion choices.
|
||||||
|
|
||||||
Embedded language highlighting:
|
Embedded language highlighting:
|
||||||
|
|
||||||
- `view { ... }` uses HTML highlighting.
|
- `view { ... }` uses HTML highlighting.
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "wrnexus",
|
"name": "wrnexus",
|
||||||
"version": "0.2.11",
|
"version": "0.2.12",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "wrnexus",
|
"name": "wrnexus",
|
||||||
"version": "0.2.11",
|
"version": "0.2.12",
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vscode/vsce": "^3.9.2"
|
"@vscode/vsce": "^3.9.2"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "wrnexus",
|
"name": "wrnexus",
|
||||||
"displayName": "WRNexus Language Support",
|
"displayName": "WRNexus Language Support",
|
||||||
"description": "Complete language support for WRNexus .wrn files, including highlighting, formatting, diagnostics, snippets, lifecycle hooks, state watchers, component functions, completions, and definition navigation.",
|
"description": "Complete language support for WRNexus .wrn files, including highlighting, formatting, diagnostics, snippets, lifecycle hooks, state watchers, component functions, completions, and definition navigation.",
|
||||||
"version": "0.2.11",
|
"version": "0.2.12",
|
||||||
"publisher": "wrnexus",
|
"publisher": "wrnexus",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
|
|||||||
@@ -0,0 +1,244 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const vscode = require("vscode");
|
||||||
|
const {
|
||||||
|
parseComponentMetadata,
|
||||||
|
parseComponentTags,
|
||||||
|
validateComponentTags,
|
||||||
|
} = require("./component-metadata");
|
||||||
|
|
||||||
|
let catalogPromise = null;
|
||||||
|
|
||||||
|
async function readMetadata(uri) {
|
||||||
|
try {
|
||||||
|
const document = await vscode.workspace.openTextDocument(uri);
|
||||||
|
return parseComponentMetadata(document.getText(), uri);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function installedUiUris() {
|
||||||
|
const uris = [];
|
||||||
|
for (const folder of vscode.workspace.workspaceFolders || []) {
|
||||||
|
const directory = vscode.Uri.joinPath(
|
||||||
|
folder.uri,
|
||||||
|
"node_modules",
|
||||||
|
"@wrnexus",
|
||||||
|
"ui",
|
||||||
|
"components",
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
for (const [name, type] of await vscode.workspace.fs.readDirectory(directory)) {
|
||||||
|
if (type === vscode.FileType.File && name.endsWith(".wrn")) {
|
||||||
|
uris.push(vscode.Uri.joinPath(directory, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// The UI package is optional; app-local components still work.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uris;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadComponentCatalog() {
|
||||||
|
const localUris = await vscode.workspace.findFiles(
|
||||||
|
"**/*.wrn",
|
||||||
|
"**/{node_modules,dist,.wrnexus,.git}/**",
|
||||||
|
);
|
||||||
|
const uiUris = await installedUiUris();
|
||||||
|
const metadata = await Promise.all([...uiUris, ...localUris].map(readMetadata));
|
||||||
|
const catalog = new Map();
|
||||||
|
for (const component of metadata) {
|
||||||
|
if (component && (component.kind === "component" || component.kind === "layout")) {
|
||||||
|
catalog.set(component.name, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentCatalog() {
|
||||||
|
catalogPromise ||= loadComponentCatalog();
|
||||||
|
return catalogPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invalidateCatalog() {
|
||||||
|
catalogPromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openingTagAt(source, offset) {
|
||||||
|
const opening = source.lastIndexOf("<", offset);
|
||||||
|
const closing = source.lastIndexOf(">", offset);
|
||||||
|
if (opening <= closing) return null;
|
||||||
|
const fragment = source.slice(opening, offset);
|
||||||
|
const match = /^<([A-Z][A-Za-z0-9_$]*)\b/.exec(fragment);
|
||||||
|
return match ? { name: match[1], fragment } : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function propSnippet(prop) {
|
||||||
|
if (prop.type === "boolean") return `${prop.name}="{\${1:false}}"`;
|
||||||
|
if (prop.type === "number") return `${prop.name}="{\${1:0}}"`;
|
||||||
|
if (prop.type === "array") return `${prop.name}="{[\${1}]}"`;
|
||||||
|
if (prop.type === "object") return `${prop.name}="{{ \${1} }}"`;
|
||||||
|
if (prop.options.length > 0) return `${prop.name}="\${1|${prop.options.join(",")}|}"`;
|
||||||
|
return `${prop.name}="\${1}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function propDocumentation(prop) {
|
||||||
|
const lines = [
|
||||||
|
`**${prop.name}** — \`${prop.type}\` ${prop.required ? "**required**" : "optional"}`,
|
||||||
|
];
|
||||||
|
if (!prop.required) lines.push(`Default: \`${prop.defaultValue}\``);
|
||||||
|
if (prop.options.length > 0)
|
||||||
|
lines.push(`Allowed: ${prop.options.map((v) => `\`${v}\``).join(", ")}`);
|
||||||
|
return lines.join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function provideComponentCompletions(document, position) {
|
||||||
|
const source = document.getText();
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const tag = openingTagAt(source, offset);
|
||||||
|
const catalog = await componentCatalog();
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
if (tag) {
|
||||||
|
const component = catalog.get(tag.name);
|
||||||
|
if (!component) return items;
|
||||||
|
const used = new Set([...tag.fragment.matchAll(/\s([^\s=/>]+)\s*=/g)].map((m) => m[1]));
|
||||||
|
for (const prop of component.props) {
|
||||||
|
if (used.has(prop.name)) continue;
|
||||||
|
const item = new vscode.CompletionItem(prop.name, vscode.CompletionItemKind.Property);
|
||||||
|
item.detail = `${prop.required ? "required" : "optional"} · ${prop.type}`;
|
||||||
|
item.documentation = new vscode.MarkdownString(propDocumentation(prop));
|
||||||
|
item.insertText = new vscode.SnippetString(propSnippet(prop));
|
||||||
|
item.sortText = `${prop.required ? "0" : "1"}-${prop.name}`;
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prefix = source.slice(Math.max(0, offset - 80), offset);
|
||||||
|
if (!/<[A-Za-z0-9_$]*$/.test(prefix)) return items;
|
||||||
|
for (const component of catalog.values()) {
|
||||||
|
const item = new vscode.CompletionItem(component.name, vscode.CompletionItemKind.Class);
|
||||||
|
const required = component.props.filter((prop) => prop.required);
|
||||||
|
item.detail = `WRN ${component.kind} · ${component.props.length} props`;
|
||||||
|
item.documentation = new vscode.MarkdownString(componentMarkdown(component));
|
||||||
|
item.insertText = new vscode.SnippetString(
|
||||||
|
`${component.name}${required.map((prop, index) => ` ${prop.name}="\${${index + 1}}"`).join("")} />`,
|
||||||
|
);
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
function componentMarkdown(component) {
|
||||||
|
const lines = [
|
||||||
|
`### <${component.name}>`,
|
||||||
|
"",
|
||||||
|
"| Prop | Type | Required | Default / options |",
|
||||||
|
"|---|---|---:|---|",
|
||||||
|
];
|
||||||
|
for (const prop of component.props) {
|
||||||
|
const detail =
|
||||||
|
prop.options.length > 0
|
||||||
|
? prop.options.join(" \\| ")
|
||||||
|
: prop.required
|
||||||
|
? "—"
|
||||||
|
: prop.defaultValue;
|
||||||
|
lines.push(
|
||||||
|
`| \`${prop.name}\` | \`${prop.type}\` | ${prop.required ? "yes" : "no"} | ${detail} |`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return lines.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function provideComponentHover(document, position) {
|
||||||
|
const source = document.getText();
|
||||||
|
const offset = document.offsetAt(position);
|
||||||
|
const catalog = await componentCatalog();
|
||||||
|
const tag = parseComponentTags(source).find(
|
||||||
|
(candidate) => offset >= candidate.start && offset <= candidate.end,
|
||||||
|
);
|
||||||
|
if (!tag) return null;
|
||||||
|
const component = catalog.get(tag.name);
|
||||||
|
if (!component) return null;
|
||||||
|
const attribute = tag.attributes.find(
|
||||||
|
(candidate) => offset >= candidate.nameStart && offset <= candidate.nameEnd,
|
||||||
|
);
|
||||||
|
const markdown = attribute
|
||||||
|
? propDocumentation(
|
||||||
|
component.props.find((prop) => prop.name === attribute.name) || {
|
||||||
|
name: attribute.name,
|
||||||
|
type: "unknown",
|
||||||
|
required: false,
|
||||||
|
defaultValue: "unknown",
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: componentMarkdown(component);
|
||||||
|
return new vscode.Hover(new vscode.MarkdownString(markdown));
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerComponentIntelligence(context) {
|
||||||
|
const diagnostics = vscode.languages.createDiagnosticCollection("wrnexus-components");
|
||||||
|
|
||||||
|
const update = async (document) => {
|
||||||
|
if (document.languageId !== "wrn") return;
|
||||||
|
const catalog = await componentCatalog();
|
||||||
|
const results = validateComponentTags(document.getText(), catalog).map((result) => {
|
||||||
|
const diagnostic = new vscode.Diagnostic(
|
||||||
|
new vscode.Range(document.positionAt(result.start), document.positionAt(result.end)),
|
||||||
|
result.message,
|
||||||
|
result.severity === "error"
|
||||||
|
? vscode.DiagnosticSeverity.Error
|
||||||
|
: vscode.DiagnosticSeverity.Warning,
|
||||||
|
);
|
||||||
|
diagnostic.source = "WRNexus Components";
|
||||||
|
diagnostic.code = result.code;
|
||||||
|
return diagnostic;
|
||||||
|
});
|
||||||
|
diagnostics.set(document.uri, results);
|
||||||
|
};
|
||||||
|
|
||||||
|
const watcher = vscode.workspace.createFileSystemWatcher("**/*.wrn");
|
||||||
|
const refreshOpenDocuments = () => {
|
||||||
|
invalidateCatalog();
|
||||||
|
for (const document of vscode.workspace.textDocuments) void update(document);
|
||||||
|
};
|
||||||
|
|
||||||
|
context.subscriptions.push(
|
||||||
|
diagnostics,
|
||||||
|
watcher,
|
||||||
|
watcher.onDidCreate(refreshOpenDocuments),
|
||||||
|
watcher.onDidChange(refreshOpenDocuments),
|
||||||
|
watcher.onDidDelete(refreshOpenDocuments),
|
||||||
|
vscode.workspace.onDidOpenTextDocument(update),
|
||||||
|
vscode.workspace.onDidChangeTextDocument((event) => void update(event.document)),
|
||||||
|
vscode.workspace.onDidSaveTextDocument((document) => {
|
||||||
|
invalidateCatalog();
|
||||||
|
void update(document);
|
||||||
|
}),
|
||||||
|
vscode.workspace.onDidCloseTextDocument((document) => diagnostics.delete(document.uri)),
|
||||||
|
vscode.languages.registerCompletionItemProvider(
|
||||||
|
{ language: "wrn" },
|
||||||
|
{ provideCompletionItems: provideComponentCompletions },
|
||||||
|
"<",
|
||||||
|
" ",
|
||||||
|
),
|
||||||
|
vscode.languages.registerHoverProvider(
|
||||||
|
{ language: "wrn" },
|
||||||
|
{ provideHover: provideComponentHover },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const document of vscode.workspace.textDocuments) void update(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
componentMarkdown,
|
||||||
|
openingTagAt,
|
||||||
|
propDocumentation,
|
||||||
|
propSnippet,
|
||||||
|
registerComponentIntelligence,
|
||||||
|
};
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const COMPONENT_DECLARATION = /\b(component|layout)\s+([A-Za-z_$][\w$]*)\s*\{/;
|
||||||
|
|
||||||
|
function findMatchingBrace(source, openingBrace) {
|
||||||
|
let depth = 0;
|
||||||
|
let quote = null;
|
||||||
|
let escaped = false;
|
||||||
|
|
||||||
|
for (let index = openingBrace; index < source.length; index += 1) {
|
||||||
|
const character = source[index];
|
||||||
|
if (quote) {
|
||||||
|
if (escaped) escaped = false;
|
||||||
|
else if (character === "\\") escaped = true;
|
||||||
|
else if (character === quote) quote = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (character === '"' || character === "'" || character === "`") quote = character;
|
||||||
|
else if (character === "{") depth += 1;
|
||||||
|
else if (character === "}" && --depth === 0) return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferType(defaultValue) {
|
||||||
|
const value = defaultValue.trim();
|
||||||
|
if (value === "undefined") return "unknown";
|
||||||
|
if (/^(?:true|false)$/.test(value)) return "boolean";
|
||||||
|
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(value)) return "number";
|
||||||
|
if (/^["'`]/.test(value)) return "string";
|
||||||
|
if (value.startsWith("[")) return "array";
|
||||||
|
if (value.startsWith("{")) return "object";
|
||||||
|
if (value === "null") return "null";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringLiteral(value) {
|
||||||
|
const match = /^(?:"([\s\S]*)"|'([\s\S]*)'|`([\s\S]*)`)$/.exec(value.trim());
|
||||||
|
return match ? (match[1] ?? match[2] ?? match[3]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferOptions(source, propName) {
|
||||||
|
const escaped = propName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const options = new Set();
|
||||||
|
const comparisons = new RegExp(`\\b${escaped}\\s*(?:===|!==|==|!=)\\s*(["'])(.*?)\\1`, "g");
|
||||||
|
let match;
|
||||||
|
while ((match = comparisons.exec(source)) !== null) options.add(match[2]);
|
||||||
|
return [...options].sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseComponentMetadata(source, uri = null) {
|
||||||
|
const declaration = COMPONENT_DECLARATION.exec(source);
|
||||||
|
if (!declaration) return null;
|
||||||
|
|
||||||
|
const propsKeyword = /\bprops\s*\{/.exec(source.slice(declaration.index));
|
||||||
|
const props = [];
|
||||||
|
if (propsKeyword) {
|
||||||
|
const start = declaration.index + propsKeyword.index;
|
||||||
|
const openingBrace = source.indexOf("{", start);
|
||||||
|
const closingBrace = findMatchingBrace(source, openingBrace);
|
||||||
|
const bodyEnd = closingBrace === -1 ? source.length : closingBrace;
|
||||||
|
const body = source.slice(openingBrace + 1, bodyEnd);
|
||||||
|
const linePattern = /^(?:\s*\/\/\s*@required\s*\r?\n)?\s*([A-Za-z_$][\w$]*)\s*=\s*(.*?)\s*$/gm;
|
||||||
|
let propMatch;
|
||||||
|
while ((propMatch = linePattern.exec(body)) !== null) {
|
||||||
|
const defaultValue = propMatch[2];
|
||||||
|
const name = propMatch[1];
|
||||||
|
props.push({
|
||||||
|
name,
|
||||||
|
defaultValue,
|
||||||
|
required: defaultValue.trim() === "undefined" || /^\s*\/\/\s*@required/m.test(propMatch[0]),
|
||||||
|
type: inferType(defaultValue),
|
||||||
|
options: inferOptions(source, name),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { kind: declaration[1], name: declaration[2], props, uri };
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrapAttributeValue(value) {
|
||||||
|
const trimmed = value.trim();
|
||||||
|
if (trimmed.startsWith("{") && trimmed.endsWith("}")) return trimmed.slice(1, -1).trim();
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function attributeValueType(value) {
|
||||||
|
const unwrapped = unwrapAttributeValue(value);
|
||||||
|
if (/^(?:true|false)$/.test(unwrapped)) return "boolean";
|
||||||
|
if (/^-?(?:\d+\.?\d*|\.\d+)$/.test(unwrapped)) return "number";
|
||||||
|
if (unwrapped.startsWith("[")) return "array";
|
||||||
|
if (unwrapped.startsWith("{")) return "object";
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTypeCompatible(prop, value) {
|
||||||
|
if (prop.type === "unknown" || prop.type === "null") return true;
|
||||||
|
const actual = attributeValueType(value);
|
||||||
|
if (prop.type === actual) return true;
|
||||||
|
if (prop.type === "number" && actual === "string") return Number.isFinite(Number(value));
|
||||||
|
if (prop.type === "boolean" && actual === "string")
|
||||||
|
return /^(?:true|false|1|0|yes|no|on|off)?$/i.test(value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseComponentTags(source) {
|
||||||
|
const tags = [];
|
||||||
|
const pattern = /<([A-Z][A-Za-z0-9_$]*)(\s[\s\S]*?)?\s*\/?>/g;
|
||||||
|
let match;
|
||||||
|
while ((match = pattern.exec(source)) !== null) {
|
||||||
|
const attributes = [];
|
||||||
|
const attributeSource = match[2] || "";
|
||||||
|
const attributeOffset = match.index + match[0].indexOf(attributeSource);
|
||||||
|
const attributePattern = /([^\s=/>]+)\s*=\s*(["'])([\s\S]*?)\2/g;
|
||||||
|
let attributeMatch;
|
||||||
|
while ((attributeMatch = attributePattern.exec(attributeSource)) !== null) {
|
||||||
|
const nameStart = attributeOffset + attributeMatch.index;
|
||||||
|
attributes.push({
|
||||||
|
name: attributeMatch[1],
|
||||||
|
value: attributeMatch[3],
|
||||||
|
nameStart,
|
||||||
|
nameEnd: nameStart + attributeMatch[1].length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
tags.push({
|
||||||
|
name: match[1],
|
||||||
|
start: match.index,
|
||||||
|
end: match.index + match[0].length,
|
||||||
|
nameStart: match.index + 1,
|
||||||
|
nameEnd: match.index + 1 + match[1].length,
|
||||||
|
attributes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateComponentTags(source, components) {
|
||||||
|
const diagnostics = [];
|
||||||
|
for (const tag of parseComponentTags(source)) {
|
||||||
|
const component = components.get(tag.name);
|
||||||
|
if (!component) continue;
|
||||||
|
const provided = new Map(tag.attributes.map((attribute) => [attribute.name, attribute]));
|
||||||
|
const declared = new Map(component.props.map((prop) => [prop.name, prop]));
|
||||||
|
|
||||||
|
for (const prop of component.props) {
|
||||||
|
if (prop.required && !provided.has(prop.name)) {
|
||||||
|
diagnostics.push({
|
||||||
|
severity: "error",
|
||||||
|
code: "wrn-missing-component-prop",
|
||||||
|
message: `<${tag.name}> requires prop \`${prop.name}\` (${prop.type}).`,
|
||||||
|
start: tag.nameStart,
|
||||||
|
end: tag.nameEnd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const attribute of tag.attributes) {
|
||||||
|
const prop = declared.get(attribute.name);
|
||||||
|
if (!prop) {
|
||||||
|
diagnostics.push({
|
||||||
|
severity: "warning",
|
||||||
|
code: "wrn-unknown-component-prop",
|
||||||
|
message: `Unknown prop \`${attribute.name}\` on <${tag.name}>.`,
|
||||||
|
start: attribute.nameStart,
|
||||||
|
end: attribute.nameEnd,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isTypeCompatible(prop, attribute.value)) {
|
||||||
|
diagnostics.push({
|
||||||
|
severity: "error",
|
||||||
|
code: "wrn-component-prop-type",
|
||||||
|
message: `Prop \`${attribute.name}\` on <${tag.name}> expects ${prop.type}, but received ${attributeValueType(attribute.value)}.`,
|
||||||
|
start: attribute.nameStart,
|
||||||
|
end: attribute.nameEnd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const literal = stringLiteral(attribute.value) ?? attribute.value;
|
||||||
|
if (prop.options.length > 0 && !prop.options.includes(literal)) {
|
||||||
|
diagnostics.push({
|
||||||
|
severity: "warning",
|
||||||
|
code: "wrn-component-prop-option",
|
||||||
|
message: `Prop \`${attribute.name}\` should be one of: ${prop.options.join(", ")}.`,
|
||||||
|
start: attribute.nameStart,
|
||||||
|
end: attribute.nameEnd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diagnostics;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
attributeValueType,
|
||||||
|
inferType,
|
||||||
|
isTypeCompatible,
|
||||||
|
parseComponentMetadata,
|
||||||
|
parseComponentTags,
|
||||||
|
validateComponentTags,
|
||||||
|
};
|
||||||
@@ -9,6 +9,8 @@ const { registerCompletionProvider } = require("./completion");
|
|||||||
|
|
||||||
const { registerDefinitionProvider } = require("./definition");
|
const { registerDefinitionProvider } = require("./definition");
|
||||||
|
|
||||||
|
const { registerComponentIntelligence } = require("./component-intelligence");
|
||||||
|
|
||||||
const { registerDiagnostics } = require("./diagnostics");
|
const { registerDiagnostics } = require("./diagnostics");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -337,6 +339,7 @@ function activate(context) {
|
|||||||
registerCompilerDiagnostics(context);
|
registerCompilerDiagnostics(context);
|
||||||
registerCompletionProvider(context);
|
registerCompletionProvider(context);
|
||||||
registerDefinitionProvider(context);
|
registerDefinitionProvider(context);
|
||||||
|
registerComponentIntelligence(context);
|
||||||
registerFormatter(context);
|
registerFormatter(context);
|
||||||
registerSemanticTokens(context);
|
registerSemanticTokens(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { test } = require("node:test");
|
||||||
|
const {
|
||||||
|
attributeValueType,
|
||||||
|
parseComponentMetadata,
|
||||||
|
parseComponentTags,
|
||||||
|
validateComponentTags,
|
||||||
|
} = require("../src/component-metadata");
|
||||||
|
|
||||||
|
const source = `component Button {
|
||||||
|
props {
|
||||||
|
// @required
|
||||||
|
label = ""
|
||||||
|
variant = "primary"
|
||||||
|
disabled = false
|
||||||
|
count = 0
|
||||||
|
items = []
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<button class="{variant === 'primary' ? 'brand' : variant === 'danger' ? 'danger' : ''}">{label}</button>
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
test("extracts component prop types, required props, defaults, and options", () => {
|
||||||
|
const component = parseComponentMetadata(source);
|
||||||
|
|
||||||
|
assert.equal(component.name, "Button");
|
||||||
|
assert.deepEqual(
|
||||||
|
component.props.map(({ name, type, required, options }) => ({ name, type, required, options })),
|
||||||
|
[
|
||||||
|
{ name: "label", type: "string", required: true, options: [] },
|
||||||
|
{ name: "variant", type: "string", required: false, options: ["danger", "primary"] },
|
||||||
|
{ name: "disabled", type: "boolean", required: false, options: [] },
|
||||||
|
{ name: "count", type: "number", required: false, options: [] },
|
||||||
|
{ name: "items", type: "array", required: false, options: [] },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("parses multiline component tags and their attribute locations", () => {
|
||||||
|
const page = `<Button
|
||||||
|
label="Save"
|
||||||
|
disabled="{false}"
|
||||||
|
/>`;
|
||||||
|
const [tag] = parseComponentTags(page);
|
||||||
|
|
||||||
|
assert.equal(tag.name, "Button");
|
||||||
|
assert.deepEqual(
|
||||||
|
tag.attributes.map(({ name, value }) => ({ name, value })),
|
||||||
|
[
|
||||||
|
{ name: "label", value: "Save" },
|
||||||
|
{ name: "disabled", value: "{false}" },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("reports missing, unknown, mismatched, and invalid-option component props", () => {
|
||||||
|
const catalog = new Map([["Button", parseComponentMetadata(source)]]);
|
||||||
|
const diagnostics = validateComponentTags(
|
||||||
|
`<Button variant="quiet" count="many" extra="value" />`,
|
||||||
|
catalog,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
diagnostics.map(({ code, severity }) => ({ code, severity })),
|
||||||
|
[
|
||||||
|
{ code: "wrn-missing-component-prop", severity: "error" },
|
||||||
|
{ code: "wrn-component-prop-option", severity: "warning" },
|
||||||
|
{ code: "wrn-component-prop-type", severity: "error" },
|
||||||
|
{ code: "wrn-unknown-component-prop", severity: "warning" },
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("recognizes literal attribute value types", () => {
|
||||||
|
assert.equal(attributeValueType("{false}"), "boolean");
|
||||||
|
assert.equal(attributeValueType("{42}"), "number");
|
||||||
|
assert.equal(attributeValueType("{[1, 2]}"), "array");
|
||||||
|
assert.equal(attributeValueType("plain"), "string");
|
||||||
|
});
|
||||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ai",
|
"name": "@wrnexus/ai",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/authz",
|
"name": "@wrnexus/authz",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/cli",
|
"name": "@wrnexus/cli",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -549,6 +549,14 @@ const MIGRATIONS: Migration[] = [
|
|||||||
// No generated application files require automatic migration.
|
// No generated application files require automatic migration.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
version: "0.2.54",
|
||||||
|
id: "essential-ui-component-system",
|
||||||
|
description: "Adds the PDF-defined minimum and essential shared UI component system.",
|
||||||
|
apply() {
|
||||||
|
// No generated application files require automatic migration.
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/** Release tooling uses this to require an explicit migration entry per version. */
|
/** Release tooling uses this to require an explicit migration entry per version. */
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/compiler",
|
"name": "@wrnexus/compiler",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/core",
|
"name": "@wrnexus/core",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/csr",
|
"name": "@wrnexus/csr",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/db",
|
"name": "@wrnexus/db",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/dev-server",
|
"name": "@wrnexus/dev-server",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/encryption",
|
"name": "@wrnexus/encryption",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/helpers",
|
"name": "@wrnexus/helpers",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/i18n",
|
"name": "@wrnexus/i18n",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/jwt",
|
"name": "@wrnexus/jwt",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/mobile",
|
"name": "@wrnexus/mobile",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/native",
|
"name": "@wrnexus/native",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/oauth",
|
"name": "@wrnexus/oauth",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/pubsub",
|
"name": "@wrnexus/pubsub",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/queue",
|
"name": "@wrnexus/queue",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/reactive",
|
"name": "@wrnexus/reactive",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/router",
|
"name": "@wrnexus/router",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/ssr",
|
"name": "@wrnexus/ssr",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/styles",
|
"name": "@wrnexus/styles",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/test",
|
"name": "@wrnexus/test",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@wrnexus/tracking",
|
"name": "@wrnexus/tracking",
|
||||||
"version": "0.2.53",
|
"version": "0.2.54",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
|
|||||||
@@ -67,6 +67,17 @@ body from either a named prop or the default slot.
|
|||||||
Also shipped: `select`, `radio`, `switch`, `progress`, `tag`, `skeleton`,
|
Also shipped: `select`, `radio`, `switch`, `progress`, `tag`, `skeleton`,
|
||||||
`tooltip`, `table`, `FAQAccordion`, `AnnouncementBar`, and `BackToTop`.
|
`tooltip`, `table`, `FAQAccordion`, `AnnouncementBar`, and `BackToTop`.
|
||||||
|
|
||||||
|
The PDF-defined minimum release and essential build-first set also includes
|
||||||
|
typed typography, form primitives, loading actions, combobox and multi-select,
|
||||||
|
time/date-time and recurring schedule controls, confirmation dialogs, data
|
||||||
|
tables, filters, desktop/mobile navigation, mega menus, marketing/product/legal
|
||||||
|
page shells, product and metric cards, FAQ composition, pricing comparison,
|
||||||
|
SDK tabs, legal navigation, and cookie preferences.
|
||||||
|
|
||||||
|
`Seo` and `StructuredData` remain framework/page concerns rather than body
|
||||||
|
components: use the native page `seo { ... }` block and document-head APIs so
|
||||||
|
metadata is emitted in `<head>` instead of invalid component markup.
|
||||||
|
|
||||||
The authoritative, always-current list is `uiComponentNames()` (below), which reads
|
The authoritative, always-current list is `uiComponentNames()` (below), which reads
|
||||||
the component directory at runtime.
|
the component directory at runtime.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component Combobox {
|
||||||
|
props {
|
||||||
|
id = "combobox"
|
||||||
|
name = ""
|
||||||
|
label = "Choose an option"
|
||||||
|
placeholder = "Search options"
|
||||||
|
options = []
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><input id="{id}" name="{name}" list="{id}-options" placeholder="{placeholder}" required="{required}" disabled="{disabled}" role="combobox" aria-autocomplete="list" class="wire-input" /><datalist id="{id}-options">{#each options as option}<option value="{option.value}">{option.label}</option>{/each}</datalist></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
component ConfirmationDialog {
|
||||||
|
props {
|
||||||
|
title = "Confirm action"
|
||||||
|
description = "Are you sure you want to continue?"
|
||||||
|
confirmLabel = "Confirm"
|
||||||
|
cancelLabel = "Cancel"
|
||||||
|
danger = false
|
||||||
|
open = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state visible = open
|
||||||
|
view {
|
||||||
|
<div data-show="visible" class="wire-overlay {class}" role="presentation"><section role="alertdialog" aria-modal="true" aria-labelledby="confirmation-title" aria-describedby="confirmation-description" class="wire-dialog"><h2 id="confirmation-title" class="wire-dialog__title">{title}</h2><p id="confirmation-description" class="wire-dialog__description">{description}</p><div class="wire-dialog__actions"><button type="button" class="wire-btn wire-btn--ghost" @click="visible = false">{cancelLabel}</button><button type="button" class="wire-btn {danger ? 'wire-btn--danger' : 'wire-btn--primary'}"><slot name="confirm">{confirmLabel}</slot></button></div></section></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
component CookiePreferencesDialog {
|
||||||
|
props {
|
||||||
|
title = "Cookie preferences"
|
||||||
|
description = "Choose which optional cookies you allow. Essential cookies are always enabled."
|
||||||
|
saveLabel = "Save preferences"
|
||||||
|
acceptLabel = "Accept all"
|
||||||
|
rejectLabel = "Reject optional"
|
||||||
|
open = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
state visible = open
|
||||||
|
state analytics = false
|
||||||
|
state marketing = false
|
||||||
|
view {
|
||||||
|
<div data-show="visible" class="wire-overlay {class}" role="presentation"><section role="dialog" aria-modal="true" aria-labelledby="cookie-preferences-title" class="wire-dialog wire-cookie-dialog"><h2 id="cookie-preferences-title" class="wire-dialog__title">{title}</h2><p class="wire-dialog__description">{description}</p><div class="wire-cookie-list"><label class="wire-cookie-option"><span><strong>Essential</strong><small>Required for security and core functionality.</small></span><input type="checkbox" checked disabled /></label><label class="wire-cookie-option"><span><strong>Analytics</strong><small>Helps improve product performance.</small></span><input type="checkbox" checked="{analytics}" @change="analytics = event.target.checked" /></label><label class="wire-cookie-option"><span><strong>Marketing</strong><small>Supports relevant campaigns and measurement.</small></span><input type="checkbox" checked="{marketing}" @change="marketing = event.target.checked" /></label></div><div class="wire-dialog__actions wire-dialog__actions--wrap"><button type="button" class="wire-btn wire-btn--ghost" @click="analytics = false; marketing = false">{rejectLabel}</button><button type="button" class="wire-btn wire-btn--default" @click="analytics = true; marketing = true">{acceptLabel}</button><button type="button" class="wire-btn wire-btn--primary" @click="visible = false">{saveLabel}</button></div></section></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
component DataTable {
|
||||||
|
props {
|
||||||
|
caption = "Data table"
|
||||||
|
columns = []
|
||||||
|
rows = []
|
||||||
|
emptyMessage = "No data available."
|
||||||
|
striped = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view {
|
||||||
|
<div class="wire-table-wrap {class}" tabindex="0" role="region" aria-label="{caption}"><table class="wire-table {striped ? 'wire-table--striped' : ''}"><caption class="wire-visually-hidden">{caption}</caption><thead><tr>{#each columns as column}<th scope="col">{column.label}</th>{/each}</tr></thead><tbody>{#each rows as row}<tr>{#each columns as column}<td>{row[column.key]}</td>{/each}</tr>{:empty}<tr><td colspan="{columns.length}">{emptyMessage}</td></tr>{/each}</tbody></table></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
component DateTimePicker {
|
||||||
|
props {
|
||||||
|
id = "datetime-picker"
|
||||||
|
name = "datetime"
|
||||||
|
label = "Date and time"
|
||||||
|
value = ""
|
||||||
|
min = ""
|
||||||
|
max = ""
|
||||||
|
required = false
|
||||||
|
disabled = false
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <div class="wire-field {class}"><label for="{id}" class="wire-label">{label}</label><input id="{id}" name="{name}" type="datetime-local" value="{value}" min="{min}" max="{max}" required="{required}" disabled="{disabled}" class="wire-input" /></div> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
component DesktopNavigation {
|
||||||
|
props {
|
||||||
|
label = "Primary navigation"
|
||||||
|
items = []
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <nav aria-label="{label}" class="wire-desktop-nav {class}"><ul>{#each items as item}<li><a href="{item.href}" aria-current="{item.current ? 'page' : 'false'}">{item.label}</a></li>{/each}</ul></nav> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
component FAQ {
|
||||||
|
props {
|
||||||
|
eyebrow = ""
|
||||||
|
title = "Frequently asked questions"
|
||||||
|
description = ""
|
||||||
|
items = []
|
||||||
|
centered = true
|
||||||
|
allowMultiple = false
|
||||||
|
defaultOpenIndex = 0
|
||||||
|
}
|
||||||
|
view { <FAQAccordion eyebrow="{eyebrow}" title="{title}" description="{description}" items="{items}" centered="{centered}" allowMultiple="{allowMultiple}" defaultOpenIndex="{defaultOpenIndex}" /> }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
component FilterBar {
|
||||||
|
props {
|
||||||
|
label = "Filters"
|
||||||
|
clearLabel = "Clear filters"
|
||||||
|
showClear = true
|
||||||
|
class = ""
|
||||||
|
}
|
||||||
|
view { <section aria-label="{label}" class="wire-filter-bar {class}"><div class="wire-filter-bar__controls"><slot /></div><button data-show="showClear" type="reset" class="wire-btn wire-btn--ghost wire-btn--sm">{clearLabel}</button></section> }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user