chore(release): stage 0.8.5 package tarballs
Quality / quality (ubuntu-latest) (push) Failing after 12m26s
Quality / quality (windows-latest) (push) Canceled after 0s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 15:16:43 +05:30
co-authored by Claude Opus 5
parent 1fb1a8d2d0
commit f01a308287
42 changed files with 2546 additions and 288 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/ai", "name": "@wrnexus/ai",
"version": "0.8.4", "version": "0.8.5",
"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",
+153
View File
@@ -149,3 +149,156 @@ app.put(
- **Bun-only** — like the rest of WrNexus, this package targets the Bun runtime; Node is not supported. - **Bun-only** — like the rest of WrNexus, this package targets the Bun runtime; Node is not supported.
- Works with [`@wrnexus/core`](../core) — the guards return `Middleware` and read the subject from `ctx.user` on the request `Context`. Both types are imported from `@wrnexus/core`. - Works with [`@wrnexus/core`](../core) — the guards return `Middleware` and read the subject from `ctx.user` on the request `Context`. Both types are imported from `@wrnexus/core`.
- Policy combinators (`any`, `all`) and `authorize` are async-aware, so policies may return a `Promise<boolean>` (e.g. for a database ownership check). - Policy combinators (`any`, `all`) and `authorize` are async-aware, so policies may return a `Promise<boolean>` (e.g. for a database ownership check).
## Declaring permissions
The RBAC/PBAC/ABAC surface above is the low-level toolkit. On top of it sits a
declarative **registry + catalog + store + engine**: permissions, roles, and
policies are declared once in code, merged into a frozen catalog at boot, and
resolved per-request against a pluggable `PermissionStore` that holds who has
what.
Put declarations in `app/authz/<name>.ts`; they are discovered automatically
and merged (conflicting declarations of the same permission/role/policy across
files fail the boot loudly, naming both source files).
```ts
import { defineAuthz, owner } from "@wrnexus/authz";
export default defineAuthz({
permissions: {
"post:read": { title: "View posts", public: true },
"post:delete": { title: "Delete posts", risk: "high" },
},
// "post:*" is a namespace wildcard grant, valid inside a role's list — it is
// not itself a registered permission, so it can only ever grant permissions
// that ARE declared above (e.g. "post:read", "post:delete").
roles: { editor: ["post:*"], admin: ["role:editor"] },
policies: { ownsPost: owner("id", "authorId") },
bindings: { "post:delete": ["ownsPost"] },
});
```
`public: true` means anonymous callers may hold the permission — but any
policy bound to it still runs, and can still veto the anonymous caller (e.g. a
`notBanned` policy on a public `post:preview` permission).
## Checking permissions
Register `authzMiddleware` once, in `app/middleware/`, with the merged
catalog and a `PermissionStore`. Like every other `app/middleware/*.ts` file,
the registration is an eager, module-scope call — the same shape as
`authzMiddleware({ catalog, store })` requires — so it must run after the
catalog has been populated. Both the dev server and `wrnexus build`'s
generated production entry guarantee `getAuthzCatalog()` is populated before
any app middleware module evaluates. Name the file so it sorts after whatever
middleware sets `ctx.user` (middleware runs in alphabetical filename order —
`authz.ts` after `auth.ts`, for instance).
```ts
// app/middleware/authz.ts
import { authzMiddleware, getAuthzCatalog } from "@wrnexus/authz";
import { dbPermissionStore } from "@wrnexus/authz/db";
import { getDb } from "@wrnexus/db";
export default authzMiddleware({ catalog: getAuthzCatalog(), store: dbPermissionStore(getDb()) });
```
> **`subject.id` must be a non-empty string.** The engine denies (and logs to
> stderr) whenever `ctx.user.id` is present but not a non-empty string — this
> includes the common case of an integer primary key. Coerce it before it
> reaches `ctx.user`, e.g. `user.id = String(row.id)`, or every request for
> that user denies with "Invalid subject" instead of resolving normally.
> `owner()` (the built-in ownership policy) compares subject and resource ids
> with `Object.is`, so both sides must be the same type too — `owner()` on a
> numeric `resource.authorId` against a stringified `subject.id` never
> matches even when they represent "the same" id.
There is no per-route `middleware` export — `app/middleware/*.ts` is the only
place middleware is registered. To gate part of the app, branch on the
request the same way any other conditional middleware does (compare
`app/middleware/captcha-login.ts` in the auth showcase, which branches on
method + path the same way):
```ts
// app/middleware/protect-posts.ts
import type { Context, Next } from "@wrnexus/core";
import { guardPermission } from "@wrnexus/authz";
const guardPostWrite = guardPermission("post:write");
export default function protectPosts(ctx: Context, next: Next) {
return ctx.url.pathname.startsWith("/api/posts") && ctx.req.method !== "GET"
? guardPostWrite(ctx, next)
: next();
}
```
Or check inline inside a route handler with the free function `can()`:
```ts
// app/api/posts/[id].ts
import type { Context } from "@wrnexus/core";
import { can } from "@wrnexus/authz";
export const DELETE = async (ctx: Context) => {
const post = { id: "1", authorId: "alice" }; // load your own resource here
if (!(await can(ctx, "post:delete", post))) {
return Response.json({ ok: false, error: "Forbidden" }, { status: 403 });
}
return Response.json({ ok: true });
};
```
`can()` is a free function taking `ctx`, not `ctx.can``@wrnexus/core` must
not depend on `@wrnexus/authz`, so the per-request resolver lives in
`ctx.locals` instead, reached through `can()` / `decideFor()` /
`guardPermission()` / `filterCan()`. Calling any of them before
`authzMiddleware` has run for that request throws a `WRN-AUTHZ-SETUP` error
naming the missing registration, rather than silently denying.
See `examples/auth-showcase/app/authz/showcase.ts` and
`examples/auth-showcase/app/middleware/authz.ts` for a complete, runnable
version of this wiring.
## Precedence
1. An explicit deny wins over everything, including `*` — and honours the
same namespace-wildcard matching as grants (denying `post:*` blocks
`post:comment:delete`, not just `post:*` itself).
2. A bound policy can veto a permission a role grants, and runs even for a
`public: true` permission — including for an anonymous caller.
3. Otherwise the permission must be held via a role or an explicit grant.
4. Default deny.
Every failure — an unknown permission (outside strict/dev mode), a store
outage, a thrown policy — denies rather than throwing through to the caller.
`permissionsFor()` (on the resolver returned by `createAuthzResolver`) is a
coarse hint for hiding UI (e.g. a menu section), **never authoritative**. A
`Set<string>` cannot represent "granted `post:*` except `post:delete`", so a
narrow deny beneath a broad grant is invisible to it — the set still contains
`post:*` while `can()` / `decide()` correctly refuse `post:delete`. Gate real
actions with `can()`, `decideFor()`, or `filterCan()`; never by matching
against `permissionsFor()`'s result.
## CLI
```bash
wrnexus authz list # every registered permission, role, and policy
wrnexus authz generate # app/authz/permissions.gen.ts type unions
wrnexus authz init # scaffold the assignment-table migration
```
`wrnexus authz generate`'s output is a plain `Permission | Role` string-literal
union — `can()`, `guardPermission()`, and `decideFor()` all take a bare
`string` and nothing reads this file automatically, so import it to type your
own helpers/constants against the registered catalog, e.g.:
```ts
import type { Permission } from "app/authz/permissions.gen.ts";
function guard(permission: Permission) {
return guardPermission(permission);
}
```
+9 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/authz", "name": "@wrnexus/authz",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/authz — part of the WrNexus framework.", "description": "@wrnexus/authz — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -34,8 +34,16 @@
".": { ".": {
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"import": "./dist/index.js" "import": "./dist/index.js"
},
"./db": {
"types": "./dist/db.d.ts",
"import": "./dist/db.js"
} }
}, },
"dependencies": {
"@wrnexus/core": "^0.8.5",
"@wrnexus/db": "^0.8.5"
},
"files": [ "files": [
"dist", "dist",
"README.md" "README.md"
+18 -17
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/cli", "name": "@wrnexus/cli",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/cli — part of the WrNexus framework.", "description": "@wrnexus/cli — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -44,22 +44,23 @@
"wrnexus": "./dist/index.js" "wrnexus": "./dist/index.js"
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/router": "^0.8.4", "@wrnexus/router": "^0.8.5",
"@wrnexus/csr": "^0.8.4", "@wrnexus/csr": "^0.8.5",
"@wrnexus/compiler": "^0.8.4", "@wrnexus/compiler": "^0.8.5",
"@wrnexus/styles": "^0.8.4", "@wrnexus/styles": "^0.8.5",
"@wrnexus/dev-server": "^0.8.4", "@wrnexus/dev-server": "^0.8.5",
"@wrnexus/ui": "^0.8.4", "@wrnexus/ui": "^0.8.5",
"@wrnexus/validation": "^0.8.4", "@wrnexus/validation": "^0.8.5",
"@wrnexus/i18n": "^0.8.4", "@wrnexus/i18n": "^0.8.5",
"@wrnexus/mcp": "^0.8.4", "@wrnexus/mcp": "^0.8.5",
"@wrnexus/playground": "^0.8.4", "@wrnexus/playground": "^0.8.5",
"@wrnexus/db": "^0.8.4", "@wrnexus/db": "^0.8.5",
"@wrnexus/plugin": "^0.8.4", "@wrnexus/authz": "^0.8.5",
"@wrnexus/syntax": "^0.8.4", "@wrnexus/plugin": "^0.8.5",
"@wrnexus/typecheck": "^0.8.4", "@wrnexus/syntax": "^0.8.5",
"@wrnexus/security": "^0.8.4", "@wrnexus/typecheck": "^0.8.5",
"@wrnexus/security": "^0.8.5",
"selfsigned": "^5.5.0" "selfsigned": "^5.5.0"
}, },
"files": [ "files": [
+5 -5
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/compiler", "name": "@wrnexus/compiler",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/compiler — part of the WrNexus framework.", "description": "@wrnexus/compiler — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,10 +37,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/csr": "^0.8.4", "@wrnexus/csr": "^0.8.5",
"@wrnexus/syntax": "^0.8.4", "@wrnexus/syntax": "^0.8.5",
"@wrnexus/store": "^0.8.4", "@wrnexus/store": "^0.8.5",
"@wrnexus/validation": "^0.8.4" "@wrnexus/validation": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/core", "name": "@wrnexus/core",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/core — part of the WrNexus framework.", "description": "@wrnexus/core — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/csr", "name": "@wrnexus/csr",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/csr — part of the WrNexus framework.", "description": "@wrnexus/csr — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/db", "name": "@wrnexus/db",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.", "description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.",
"license": "MIT", "license": "MIT",
+22 -20
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/dev-server", "name": "@wrnexus/dev-server",
"version": "0.8.4", "version": "0.8.5",
"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",
@@ -41,25 +41,27 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/authz": "^0.8.5",
"@wrnexus/dev-toolbar": "^0.8.4", "@wrnexus/rpc": "^0.8.5",
"@wrnexus/router": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/ssr": "^0.8.4", "@wrnexus/dev-toolbar": "^0.8.5",
"@wrnexus/csr": "^0.8.4", "@wrnexus/router": "^0.8.5",
"@wrnexus/compiler": "^0.8.4", "@wrnexus/ssr": "^0.8.5",
"@wrnexus/styles": "^0.8.4", "@wrnexus/csr": "^0.8.5",
"@wrnexus/ui": "^0.8.4", "@wrnexus/compiler": "^0.8.5",
"@wrnexus/validation": "^0.8.4", "@wrnexus/styles": "^0.8.5",
"@wrnexus/i18n": "^0.8.4", "@wrnexus/ui": "^0.8.5",
"@wrnexus/db": "^0.8.4", "@wrnexus/validation": "^0.8.5",
"@wrnexus/pubsub": "^0.8.4", "@wrnexus/i18n": "^0.8.5",
"@wrnexus/uploader": "^0.8.4", "@wrnexus/db": "^0.8.5",
"@wrnexus/plugin": "^0.8.4", "@wrnexus/pubsub": "^0.8.5",
"@wrnexus/store": "^0.8.4", "@wrnexus/uploader": "^0.8.5",
"@wrnexus/security": "^0.8.4", "@wrnexus/plugin": "^0.8.5",
"@wrnexus/observability": "^0.8.4", "@wrnexus/store": "^0.8.5",
"@wrnexus/cache": "^0.8.4", "@wrnexus/security": "^0.8.5",
"@wrnexus/pwa": "^0.8.4" "@wrnexus/observability": "^0.8.5",
"@wrnexus/cache": "^0.8.5",
"@wrnexus/pwa": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/dev-toolbar", "name": "@wrnexus/dev-toolbar",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.", "description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/encryption", "name": "@wrnexus/encryption",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.", "description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/helpers", "name": "@wrnexus/helpers",
"version": "0.8.4", "version": "0.8.5",
"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",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/i18n", "name": "@wrnexus/i18n",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "Locale loading, fallback resolution, SSR/browser translations, formatters, and WRNexusJS language components.", "description": "Locale loading, fallback resolution, SSR/browser translations, formatters, and WRNexusJS language components.",
"license": "MIT", "license": "MIT",
@@ -42,9 +42,9 @@
"./components/*": "./components/*" "./components/*": "./components/*"
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/plugin": "^0.8.4", "@wrnexus/plugin": "^0.8.5",
"@wrnexus/ui": "^0.8.4" "@wrnexus/ui": "^0.8.5"
}, },
"wrnexus": { "wrnexus": {
"plugin": { "plugin": {
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/jwt", "name": "@wrnexus/jwt",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.", "description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/mobile", "name": "@wrnexus/mobile",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/mobile — part of the WrNexus framework.", "description": "@wrnexus/mobile — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/native": "^0.8.4" "@wrnexus/native": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/native", "name": "@wrnexus/native",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/native — part of the WrNexus framework.", "description": "@wrnexus/native — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/oauth", "name": "@wrnexus/oauth",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/oauth — part of the WrNexus framework.", "description": "@wrnexus/oauth — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/jwt": "^0.8.4" "@wrnexus/jwt": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/plugin", "name": "@wrnexus/plugin",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/plugin — part of the WrNexus framework.", "description": "@wrnexus/plugin — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -49,7 +49,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/syntax": "^0.8.4" "@wrnexus/syntax": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/pubsub", "name": "@wrnexus/pubsub",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/pubsub — part of the WrNexus framework.", "description": "@wrnexus/pubsub — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/queue", "name": "@wrnexus/queue",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/queue — part of the WrNexus framework.", "description": "@wrnexus/queue — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,7 +37,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/reactive", "name": "@wrnexus/reactive",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/reactive — part of the WrNexus framework.", "description": "@wrnexus/reactive — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/router", "name": "@wrnexus/router",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/router — part of the WrNexus framework.", "description": "@wrnexus/router — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,8 +37,8 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/compiler": "^0.8.4", "@wrnexus/compiler": "^0.8.5",
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/ssr", "name": "@wrnexus/ssr",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/ssr — part of the WrNexus framework.", "description": "@wrnexus/ssr — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -45,9 +45,9 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/store": "^0.8.4", "@wrnexus/store": "^0.8.5",
"@wrnexus/security": "^0.8.4" "@wrnexus/security": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/styles", "name": "@wrnexus/styles",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/styles — part of the WrNexus framework.", "description": "@wrnexus/styles — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -37,9 +37,9 @@
} }
}, },
"dependencies": { "dependencies": {
"@wrnexus/uploader": "^0.8.4", "@wrnexus/uploader": "^0.8.5",
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/plugin": "^0.8.4" "@wrnexus/plugin": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/syntax", "name": "@wrnexus/syntax",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/syntax — part of the WrNexus framework.", "description": "@wrnexus/syntax — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/test", "name": "@wrnexus/test",
"version": "0.8.4", "version": "0.8.5",
"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 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/tracking", "name": "@wrnexus/tracking",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/tracking — part of the WrNexus framework.", "description": "@wrnexus/tracking — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
+16 -16
View File
@@ -359,6 +359,15 @@ Reusable preference switcher component.
- Slots: None - Slots: None
- Outputs: `theme({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `color({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `language({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])` - Outputs: `theme({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `color({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `language({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
### Toaster
Reusable toaster component.
- Mount: `data-component="Toaster"`
- Props: `color: string = "info"`, `size: string = "default"`, `position: string = "bottom-right"`, `duration: number = 4500`, `max: number = 4`, `pauseOnHover: boolean = true`, `showIcon: boolean = true`, `successIcon: string = ""`, `dangerIcon: string = ""`, `warningIcon: string = ""`, `infoIcon: string = ""`, `closable: boolean = true`, `showProgress: boolean = true`, `closeLabel: string = "Dismiss notification"`, `class: string = ""`
- Slots: None
- Outputs: `show({ id: number; message: string; tone: string })`, `dismiss({ id: number; reason: string })`, `action({ id: number; sourceEvent: Event })`
## Data ## Data
### MetricCard ### MetricCard
@@ -554,15 +563,6 @@ Theme-aware, responsive data map component.
- Slots: `default` - Slots: `default`
- Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)` - Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
### DataTable
Theme-aware, responsive data table component.
- Mount: `data-component="DataTable"`
- Props: `size: string = "default"`, `color: string = "primary"`, `caption: string = "Data Table"`, `columns: unknown[] = []`, `rows: unknown[] = []`, `striped: boolean = true`, `class: string = ""`
- Slots: `default`
- Outputs: `sort({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `rowClick({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `pageChange({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
### DragAndDrop ### DragAndDrop
Theme-aware, responsive drag and drop component. Theme-aware, responsive drag and drop component.
@@ -945,7 +945,7 @@ Open an accessible keyboard-aware action menu from pointer or keyboard context i
Present responsive modal side or bottom content with focus management, backdrop behavior, slots, and close events. Present responsive modal side or bottom content with focus management, backdrop behavior, slots, and close events.
- Mount: `data-component="Drawer"` - Mount: `data-component="Drawer"`
- Props: `open: boolean = false`, `defaultOpen: boolean = false`, `placement: string = "right"`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `title: string = "Drawer"`, `description: string = ""`, `icon: string = ""`, `label: string = "Drawer"`, `closeLabel: string = "Close drawer"`, `showClose: boolean = true`, `closeOnBackdrop: boolean = true`, `closeOnEscape: boolean = true`, `overlay: boolean = true`, `scrollable: boolean = true`, `triggerLabel: string = ""`, `triggerIcon: string = ""`, `class: string = ""` - Props: `open: boolean = false`, `defaultOpen: boolean = false`, `placement: string = "right"`, `size: string = "md"`, `color: string = "primary"`, `variant: string = "default"`, `title: string = "Drawer"`, `description: string = ""`, `icon: string = ""`, `label: string = "Drawer"`, `closeLabel: string = "Close drawer"`, `showClose: boolean = true`, `closeOnBackdrop: boolean = true`, `closeOnEscape: boolean = true`, `duration: number = 260`, `overlay: boolean = true`, `scrollable: boolean = true`, `triggerLabel: string = ""`, `triggerIcon: string = ""`, `class: string = ""`
- Slots: `trigger`, `header`, `default`, `footer` - Slots: `trigger`, `header`, `default`, `footer`
- Outputs: `open({ placement: string; sourceEvent: Event })`, `close({ reason: string; placement: string; sourceEvent: Event })`, `cancel({ placement: string; sourceEvent: Event })` - Outputs: `open({ placement: string; sourceEvent: Event })`, `close({ reason: string; placement: string; sourceEvent: Event })`, `cancel({ placement: string; sourceEvent: Event })`
@@ -963,7 +963,7 @@ Open an accessible anchored menu with keyboard navigation, item selection, actio
Present an accessible modal dialog with focus management, confirmation, cancellation, slots, and responsive sizing. Present an accessible modal dialog with focus management, confirmation, cancellation, slots, and responsive sizing.
- Mount: `data-component="Modal"` - Mount: `data-component="Modal"`
- Props: `open: boolean = false`, `defaultOpen: boolean = false`, `title: string = "Modal"`, `description: string = ""`, `icon: string = ""`, `label: string = "Modal dialog"`, `size: string = "md"`, `placement: string = "center"`, `color: string = "primary"`, `variant: string = "default"`, `showClose: boolean = true`, `closeLabel: string = "Close modal"`, `closeOnBackdrop: boolean = true`, `closeOnEscape: boolean = true`, `closeOnCancel: boolean = true`, `closeOnConfirm: boolean = false`, `showFooter: boolean = true`, `cancelLabel: string = "Cancel"`, `cancelIcon: string = ""`, `confirmLabel: string = "Confirm"`, `confirmIcon: string = ""`, `confirmDisabled: boolean = false`, `confirmLoading: boolean = false`, `destructive: boolean = false`, `triggerLabel: string = ""`, `triggerIcon: string = ""`, `scrollable: boolean = true`, `class: string = ""` - Props: `open: boolean = false`, `defaultOpen: boolean = false`, `title: string = "Modal"`, `description: string = ""`, `icon: string = ""`, `label: string = "Modal dialog"`, `size: string = "md"`, `placement: string = "center"`, `color: string = "primary"`, `variant: string = "default"`, `showClose: boolean = true`, `closeLabel: string = "Close modal"`, `closeOnBackdrop: boolean = true`, `closeOnEscape: boolean = true`, `closeOnCancel: boolean = true`, `closeOnConfirm: boolean = false`, `showFooter: boolean = true`, `cancelLabel: string = "Cancel"`, `cancelIcon: string = ""`, `confirmLabel: string = "Confirm"`, `confirmIcon: string = ""`, `confirmDisabled: boolean = false`, `confirmLoading: boolean = false`, `destructive: boolean = false`, `triggerLabel: string = ""`, `triggerIcon: string = ""`, `scrollable: boolean = true`, `scrollBehavior: string = "inside"`, `class: string = ""`
- Slots: `trigger`, `header`, `default`, `footer` - Slots: `trigger`, `header`, `default`, `footer`
- Outputs: `open({ sourceEvent: Event })`, `close({ reason: string; sourceEvent: Event })`, `cancel({ sourceEvent: Event })`, `confirm({ sourceEvent: Event })` - Outputs: `open({ sourceEvent: Event })`, `close({ reason: string; sourceEvent: Event })`, `cancel({ sourceEvent: Event })`, `confirm({ sourceEvent: Event })`
@@ -987,11 +987,11 @@ Show concise accessible contextual help on hover, focus, click, or controlled op
## Tables ## Tables
### Table ### DataTable
Theme-aware, responsive table component. Sortable, filterable, paginated data table with row selection.
- Mount: `data-component="Table"` - Mount: `data-component="DataTable"`
- Props: `size: string = "default"`, `color: string = "primary"`, `caption: string = "Table"`, `columns: unknown[] = []`, `rows: unknown[] = []`, `striped: boolean = true`, `class: string = ""` - Props: `color: string = "primary"`, `size: string = "default"`, `columns: unknown[] = []`, `rows: unknown[] = []`, `rowKey: string = "id"`, `remote: boolean = false`, `loadingLabel: string = "Loading"`, `errorLabel: string = "Could not load this data"`, `retryLabel: string = "Try again"`, `caption: string = ""`, `description: string = ""`, `searchable: boolean = true`, `searchPlaceholder: string = "Search"`, `paginated: boolean = true`, `pageSize: number = 10`, `paginationStyle: string = "compact"`, `pageSizes: number[] = [10, 25, 50]`, `selectable: boolean = false`, `actions: unknown[] = []`, `striped: boolean = true`, `bordered: boolean = true`, `gridlines: string = "rows"`, `density: string = "default"`, `emptyLabel: string = "No records to show"`, `noResultsLabel: string = "No records match your search"`, `clearSearchLabel: string = "Clear search"`, `stickyFirstColumn: boolean = false`, `layout: string = "rows"`, `class: string = ""`
- Slots: `default` - Slots: `default`
- Outputs: `sort({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `rowClick({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)` - Outputs: `sort({ key: string; direction: string })`, `search({ query: string })`, `pageChange({ page: number; pageSize: number })`, `select({ selected: Array<string | number>; all: boolean })`, `change({ page: number; pageSize: number; total: number; query: string; sortKey: string; sortDirection: string })`, `rowClick({ row: object; sourceEvent: Event })`, `action({ id: string; selected: Array<string | number>; rows: object[]; sourceEvent: Event })`, `request({ instanceId: number; page: number; pageSize: number; sortKey: string; sortDirection: string; query: string })`
+7 -7
View File
@@ -168,8 +168,8 @@
}, },
{ {
"name": "DataTable", "name": "DataTable",
"category": "integrations", "category": "tables",
"purpose": "Theme-aware, responsive data table component." "purpose": "Sortable, filterable, paginated data table with row selection."
}, },
{ {
"name": "DatePicker", "name": "DatePicker",
@@ -471,11 +471,6 @@
"category": "forms", "category": "forms",
"purpose": "Theme-aware, responsive switch component." "purpose": "Theme-aware, responsive switch component."
}, },
{
"name": "Table",
"category": "tables",
"purpose": "Theme-aware, responsive table component."
},
{ {
"name": "Tabs", "name": "Tabs",
"category": "navigation", "category": "navigation",
@@ -511,6 +506,11 @@
"category": "integrations", "category": "integrations",
"purpose": "Theme-aware, responsive toast notifications component." "purpose": "Theme-aware, responsive toast notifications component."
}, },
{
"name": "Toaster",
"category": "core",
"purpose": "Reusable toaster component."
},
{ {
"name": "ToggleCount", "name": "ToggleCount",
"category": "advanced-forms", "category": "advanced-forms",
+343 -98
View File
@@ -4569,16 +4569,9 @@
{ {
"name": "DataTable", "name": "DataTable",
"mount": "DataTable", "mount": "DataTable",
"category": "integrations", "category": "tables",
"purpose": "Theme-aware, responsive data table component.", "purpose": "Sortable, filterable, paginated data table with row selection.",
"props": [ "props": [
{
"name": "size",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{ {
"name": "color", "name": "color",
"type": "string", "type": "string",
@@ -4587,10 +4580,10 @@
"options": [] "options": []
}, },
{ {
"name": "caption", "name": "size",
"type": "string", "type": "string",
"required": false, "required": false,
"default": "\"Data Table\"", "default": "\"default\"",
"options": [] "options": []
}, },
{ {
@@ -4607,6 +4600,111 @@
"default": "[]", "default": "[]",
"options": [] "options": []
}, },
{
"name": "rowKey",
"type": "string",
"required": false,
"default": "\"id\"",
"options": []
},
{
"name": "remote",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "loadingLabel",
"type": "string",
"required": false,
"default": "\"Loading\"",
"options": []
},
{
"name": "errorLabel",
"type": "string",
"required": false,
"default": "\"Could not load this data\"",
"options": []
},
{
"name": "retryLabel",
"type": "string",
"required": false,
"default": "\"Try again\"",
"options": []
},
{
"name": "caption",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "description",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "searchable",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "searchPlaceholder",
"type": "string",
"required": false,
"default": "\"Search\"",
"options": []
},
{
"name": "paginated",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "pageSize",
"type": "number",
"required": false,
"default": "10",
"options": []
},
{
"name": "paginationStyle",
"type": "string",
"required": false,
"default": "\"compact\"",
"options": []
},
{
"name": "pageSizes",
"type": "number[]",
"required": false,
"default": "[10, 25, 50]",
"options": []
},
{
"name": "selectable",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "actions",
"type": "unknown[]",
"required": false,
"default": "[]",
"options": []
},
{ {
"name": "striped", "name": "striped",
"type": "boolean", "type": "boolean",
@@ -4614,6 +4712,62 @@
"default": "true", "default": "true",
"options": [] "options": []
}, },
{
"name": "bordered",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "gridlines",
"type": "string",
"required": false,
"default": "\"rows\"",
"options": []
},
{
"name": "density",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "emptyLabel",
"type": "string",
"required": false,
"default": "\"No records to show\"",
"options": []
},
{
"name": "noResultsLabel",
"type": "string",
"required": false,
"default": "\"No records match your search\"",
"options": []
},
{
"name": "clearSearchLabel",
"type": "string",
"required": false,
"default": "\"Clear search\"",
"options": []
},
{
"name": "stickyFirstColumn",
"type": "boolean",
"required": false,
"default": "false",
"options": []
},
{
"name": "layout",
"type": "string",
"required": false,
"default": "\"rows\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -4626,26 +4780,47 @@
"outputs": [ "outputs": [
{ {
"name": "sort", "name": "sort",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null" "payloadType": "{ key: string; direction: string }"
}, },
{ {
"name": "select", "name": "search",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null" "payloadType": "{ query: string }"
},
{
"name": "change",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
},
{
"name": "rowClick",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
}, },
{ {
"name": "pageChange", "name": "pageChange",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null" "payloadType": "{ page: number; pageSize: number }"
},
{
"name": "select",
"payloadType": "{ selected: Array<string | number>; all: boolean }"
},
{
"name": "change",
"payloadType": "{ page: number; pageSize: number; total: number; query: string; sortKey: string; sortDirection: string }"
},
{
"name": "rowClick",
"payloadType": "{ row: object; sourceEvent: Event }"
},
{
"name": "action",
"payloadType": "{ id: string; selected: Array<string | number>; rows: object[]; sourceEvent: Event }"
},
{
"name": "request",
"payloadType": "{ instanceId: number; page: number; pageSize: number; sortKey: string; sortDirection: string; query: string }"
} }
], ],
"events": ["sort", "select", "change", "rowClick", "pageChange"], "events": [
"sort",
"search",
"pageChange",
"select",
"change",
"rowClick",
"action",
"request"
],
"source": "components/DataTable.wrn" "source": "components/DataTable.wrn"
}, },
{ {
@@ -5220,6 +5395,13 @@
"default": "true", "default": "true",
"options": [] "options": []
}, },
{
"name": "duration",
"type": "number",
"required": false,
"default": "260",
"options": []
},
{ {
"name": "overlay", "name": "overlay",
"type": "boolean", "type": "boolean",
@@ -8736,6 +8918,13 @@
"default": "true", "default": "true",
"options": [] "options": []
}, },
{
"name": "scrollBehavior",
"type": "string",
"required": false,
"default": "\"inside\"",
"options": []
},
{ {
"name": "class", "name": "class",
"type": "string", "type": "string",
@@ -12009,80 +12198,6 @@
"events": ["input", "change", "focus", "blur"], "events": ["input", "change", "focus", "blur"],
"source": "components/Switch.wrn" "source": "components/Switch.wrn"
}, },
{
"name": "Table",
"mount": "Table",
"category": "tables",
"purpose": "Theme-aware, responsive table component.",
"props": [
{
"name": "size",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "color",
"type": "string",
"required": false,
"default": "\"primary\"",
"options": []
},
{
"name": "caption",
"type": "string",
"required": false,
"default": "\"Table\"",
"options": []
},
{
"name": "columns",
"type": "unknown[]",
"required": false,
"default": "[]",
"options": []
},
{
"name": "rows",
"type": "unknown[]",
"required": false,
"default": "[]",
"options": []
},
{
"name": "striped",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
}
],
"slots": ["default"],
"outputs": [
{
"name": "sort",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
},
{
"name": "select",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
},
{
"name": "rowClick",
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
}
],
"events": ["sort", "select", "rowClick"],
"source": "components/table.wrn"
},
{ {
"name": "Tabs", "name": "Tabs",
"mount": "Tabs", "mount": "Tabs",
@@ -12893,6 +13008,136 @@
"events": ["add", "dismiss", "clear", "action"], "events": ["add", "dismiss", "clear", "action"],
"source": "components/ToastNotifications.wrn" "source": "components/ToastNotifications.wrn"
}, },
{
"name": "Toaster",
"mount": "Toaster",
"category": "core",
"purpose": "Reusable toaster component.",
"props": [
{
"name": "color",
"type": "string",
"required": false,
"default": "\"info\"",
"options": []
},
{
"name": "size",
"type": "string",
"required": false,
"default": "\"default\"",
"options": []
},
{
"name": "position",
"type": "string",
"required": false,
"default": "\"bottom-right\"",
"options": []
},
{
"name": "duration",
"type": "number",
"required": false,
"default": "4500",
"options": []
},
{
"name": "max",
"type": "number",
"required": false,
"default": "4",
"options": []
},
{
"name": "pauseOnHover",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "showIcon",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "successIcon",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "dangerIcon",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "warningIcon",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "infoIcon",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
},
{
"name": "closable",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "showProgress",
"type": "boolean",
"required": false,
"default": "true",
"options": []
},
{
"name": "closeLabel",
"type": "string",
"required": false,
"default": "\"Dismiss notification\"",
"options": []
},
{
"name": "class",
"type": "string",
"required": false,
"default": "\"\"",
"options": []
}
],
"slots": [],
"outputs": [
{
"name": "show",
"payloadType": "{ id: number; message: string; tone: string }"
},
{
"name": "dismiss",
"payloadType": "{ id: number; reason: string }"
},
{
"name": "action",
"payloadType": "{ id: number; sourceEvent: Event }"
}
],
"events": ["show", "dismiss", "action"],
"source": "components/Toaster.wrn"
},
{ {
"name": "ToggleCount", "name": "ToggleCount",
"mount": "ToggleCount", "mount": "ToggleCount",
+7 -2
View File
@@ -45,8 +45,12 @@ component ContextMenu {
sourceEvent.preventDefault() sourceEvent.preventDefault()
} }
if (placement === "pointer" && sourceEvent) { if (placement === "pointer" && sourceEvent) {
positionX = Math.max(12, Math.min(sourceEvent.clientX || 12, window.innerWidth - 340)) // Place the menu at the pointer and let the anchored clamp in the
positionY = Math.max(12, Math.min(sourceEvent.clientY || 12, window.innerHeight - 420)) // runtime pull it back on screen once it has been laid out and can
// actually be measured. Subtracting a guessed 340x420 here instead
// pushed every menu that was not that size away from the pointer.
positionX = Math.max(12, sourceEvent.clientX || 12)
positionY = Math.max(12, sourceEvent.clientY || 12)
} }
visible = true visible = true
output.open({ output.open({
@@ -161,6 +165,7 @@ component ContextMenu {
<div <div
class="wire-context-menu__panel" class="wire-context-menu__panel"
data-wrn-anchored="true"
data-show='{open || visible}' data-show='{open || visible}'
role="menu" role="menu"
aria-label='{label}' aria-label='{label}'
File diff suppressed because it is too large Load Diff
+83 -1
View File
@@ -20,6 +20,8 @@ open: boolean = false
showClose: boolean = true showClose: boolean = true
closeOnBackdrop: boolean = true closeOnBackdrop: boolean = true
closeOnEscape: boolean = true closeOnEscape: boolean = true
// Open/close animation length in ms. 0 disables the animation entirely.
duration: number = 260
overlay: boolean = true overlay: boolean = true
scrollable: boolean = true scrollable: boolean = true
triggerLabel: string = "" triggerLabel: string = ""
@@ -79,7 +81,9 @@ open: boolean = false
data-overlay='{overlay ? "true" : "false"}' data-overlay='{overlay ? "true" : "false"}'
data-scrollable='{scrollable ? "true" : "false"}' data-scrollable='{scrollable ? "true" : "false"}'
class='wire-drawer {class}' class='wire-drawer {class}'
style='--drawer-duration: {duration}ms'
> >
{#if triggerLabel} {#if triggerLabel}
<button <button
type="button" type="button"
@@ -101,7 +105,6 @@ open: boolean = false
<div <div
class="wire-drawer__layer" class="wire-drawer__layer"
data-show='{open || visible}'
role="presentation" role="presentation"
@keydown='handleKeydown(event)' @keydown='handleKeydown(event)'
> >
@@ -218,12 +221,32 @@ open: boolean = false
cursor: pointer; cursor: pointer;
} }
/*
* The layer stays in the layout and is revealed by [data-open]; it used to
* be toggled with data-show, which sets display:none, and display cannot
* be transitioned -- the drawer simply snapped in and out. visibility is
* delayed by the duration on the way out so the panel can finish sliding
* before the layer is taken out of the hit-testing tree.
*/
.wire-drawer__layer { .wire-drawer__layer {
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: 1200; z-index: 1200;
display: flex; display: flex;
pointer-events: none; pointer-events: none;
visibility: hidden;
opacity: 0;
transition:
opacity var(--drawer-duration, 260ms) ease,
visibility 0s linear var(--drawer-duration, 260ms);
}
.wire-drawer[data-open="true"] .wire-drawer__layer {
visibility: visible;
opacity: 1;
transition:
opacity var(--drawer-duration, 260ms) ease,
visibility 0s linear 0s;
} }
.wire-drawer__backdrop { .wire-drawer__backdrop {
@@ -262,6 +285,38 @@ open: boolean = false
box-shadow: -28px 0 80px color-mix(in srgb, black 28%, transparent); box-shadow: -28px 0 80px color-mix(in srgb, black 28%, transparent);
pointer-events: auto; pointer-events: auto;
overflow: hidden; overflow: hidden;
/* Slides in from whichever edge the placement puts it on. */
transform: translateX(100%);
transition: transform var(--drawer-duration, 260ms) cubic-bezier(0.32, 0.72, 0, 1);
}
.wire-drawer[data-open="true"] .wire-drawer__panel {
transform: none;
}
.wire-drawer[data-placement="left"] .wire-drawer__panel {
transform: translateX(-100%);
}
.wire-drawer[data-placement="top"] .wire-drawer__panel {
transform: translateY(-100%);
}
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
transform: translateY(100%);
}
.wire-drawer[data-open="true"][data-placement="left"] .wire-drawer__panel,
.wire-drawer[data-open="true"][data-placement="top"] .wire-drawer__panel,
.wire-drawer[data-open="true"][data-placement="bottom"] .wire-drawer__panel {
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.wire-drawer__layer,
.wire-drawer__panel {
transition: none;
}
} }
.wire-drawer[data-size="sm"] .wire-drawer__panel { .wire-drawer[data-size="sm"] .wire-drawer__panel {
@@ -395,12 +450,19 @@ open: boolean = false
color: color-mix(in srgb, currentColor 76%, transparent); color: color-mix(in srgb, currentColor 76%, transparent);
} }
/*
* padding is reset explicitly: an app-level `button { padding: ... }` rule
* outranks the browser default and leaves this fixed-size button with a
* content box of a couple of pixels, which squeezes the icon to a sliver
* and reads as "the close button has no icon". Same trap as Modal.
*/
.wire-drawer__close { .wire-drawer__close {
appearance: none; appearance: none;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex: 0 0 auto; flex: 0 0 auto;
padding: 0;
width: 2.35rem; width: 2.35rem;
height: 2.35rem; height: 2.35rem;
color: var(--wire-color-text-muted); color: var(--wire-color-text-muted);
@@ -417,10 +479,30 @@ open: boolean = false
outline: none; outline: none;
} }
/* Never let the glyph be shrunk by the flex container. */
.wire-drawer__close svg {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
}
/*
* Slot content is authored by the host app, so the app global stylesheet
* styles it too. A bare element selector there (p { color: ... }) beats
* anything the panel merely *inherits*, which is how modal body copy ended
* up muted grey on a saturated background. State the colour explicitly;
* :where() keeps the specificity low enough that any class the app puts on
* its own slot content still wins.
*/
.wire-drawer__body { .wire-drawer__body {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
padding: 1.35rem; padding: 1.35rem;
color: var(--wire-color-text);
}
.wire-drawer__body :where(p, li, dd, dt, h1, h2, h3, h4, h5, h6, span, label, code) {
color: inherit;
} }
.wire-drawer[data-scrollable="true"] .wire-drawer__body { .wire-drawer[data-scrollable="true"] .wire-drawer__body {
+1
View File
@@ -171,6 +171,7 @@ items: unknown[] = []
<div <div
class="wire-dropdown__panel" class="wire-dropdown__panel"
data-wrn-anchored="true"
data-show='{open || visible}' data-show='{open || visible}'
role="menu" role="menu"
aria-label='{menuLabel}' aria-label='{menuLabel}'
+196 -17
View File
@@ -35,6 +35,7 @@ component Modal {
triggerLabel: string = "" triggerLabel: string = ""
triggerIcon: string = "" triggerIcon: string = ""
scrollable: boolean = true scrollable: boolean = true
scrollBehavior: string = "inside"
class: string = "" class: string = ""
} }
@@ -50,6 +51,16 @@ component Modal {
output.open({ sourceEvent: sourceEvent }) output.open({ sourceEvent: sourceEvent })
} }
// Slot content can close the modal it sits in by dispatching a bubbling
// wrnexus:modal:close event, e.g. from a form success handler:
//
// event.target.dispatchEvent(
// new CustomEvent("wrnexus:modal:close", { bubbles: true })
// )
//
// The listener is on the modal root, so the event only ever closes the
// modal the dispatching element is actually inside -- no ids to wire up
// and no way to close somebody else's modal by accident.
client function hideModal(reason, sourceEvent) { client function hideModal(reason, sourceEvent) {
visible = false visible = false
output.close({ output.close({
@@ -87,21 +98,23 @@ component Modal {
<div <div
{...attrs} {...attrs}
data-ui-component="Modal" data-ui-component="Modal"
data-open='{open || visible ? "true" : "false"}' data-open='{isOpen() ? "true" : "false"}'
data-size='{size}' data-size='{size}'
data-placement='{placement}' data-placement='{placement}'
data-color='{color}' data-color='{color}'
data-variant='{variant}' data-variant='{variant}'
data-scrollable='{scrollable ? "true" : "false"}' data-scrollable='{scrollable ? "true" : "false"}'
data-scroll='{scrollBehavior}'
data-destructive='{destructive ? "true" : "false"}' data-destructive='{destructive ? "true" : "false"}'
class='wire-modal {class}' class='wire-modal {class}'
@wrnexus:modal:close='hideModal("api", event)'
> >
{#if triggerLabel} {#if triggerLabel}
<button <button
type="button" type="button"
class="wire-modal__trigger" class="wire-modal__trigger"
aria-haspopup="dialog" aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}' aria-expanded='{isOpen() ? "true" : "false"}'
@click='showModal(event)' @click='showModal(event)'
> >
{#if triggerIcon} {#if triggerIcon}
@@ -127,7 +140,7 @@ component Modal {
<div <div
class="wire-modal__layer" class="wire-modal__layer"
data-show='{open || visible}' data-show="isOpen()"
role="presentation" role="presentation"
@keydown='handleKeydown(event)' @keydown='handleKeydown(event)'
> >
@@ -183,11 +196,20 @@ component Modal {
aria-label='{closeLabel}' aria-label='{closeLabel}'
@click='hideModal("close-button", event)' @click='hideModal("close-button", event)'
> >
<span <svg
class="icon-[lucide--x]" viewBox="0 0 24 24"
width="16"
height="16"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
aria-hidden="true" aria-hidden="true"
> >
</span> <path d="M18 6 6 18" />
<path d="M6 6 18 18" />
</svg>
</button> </button>
{/if} {/if}
</header> </header>
@@ -277,16 +299,26 @@ component Modal {
--modal-contrast: var(--wire-color-secondary-contrast); --modal-contrast: var(--wire-color-secondary-contrast);
} }
/*
* Fallbacks below (the second var() argument): the theme token generator
* (packages/styles/src/theme.ts) only emits a real -contrast token for
* primary and secondary. info, success, and danger have no contrast
* token defined at all, so var(--wire-color-info-contrast) with no
* fallback resolves to nothing, and --modal-contrast becomes invalid --
* which made confirm-button and solid-panel text unreadable. White is a
* safe default against these saturated colors until the theme package
* defines real tokens for them.
*/
.wire-modal[data-color="info"] { .wire-modal[data-color="info"] {
--modal-accent: var(--wire-color-info); --modal-accent: var(--wire-color-info);
--modal-soft: var(--wire-color-info-soft); --modal-soft: var(--wire-color-info-soft);
--modal-contrast: var(--wire-color-info-contrast); --modal-contrast: var(--wire-color-info-contrast, white);
} }
.wire-modal[data-color="success"] { .wire-modal[data-color="success"] {
--modal-accent: var(--wire-color-success); --modal-accent: var(--wire-color-success);
--modal-soft: var(--wire-color-success-soft); --modal-soft: var(--wire-color-success-soft);
--modal-contrast: var(--wire-color-success-contrast); --modal-contrast: var(--wire-color-success-contrast, white);
} }
.wire-modal[data-color="warning"] { .wire-modal[data-color="warning"] {
@@ -299,7 +331,7 @@ component Modal {
.wire-modal[data-destructive="true"] { .wire-modal[data-destructive="true"] {
--modal-accent: var(--wire-color-danger); --modal-accent: var(--wire-color-danger);
--modal-soft: var(--wire-color-danger-soft); --modal-soft: var(--wire-color-danger-soft);
--modal-contrast: var(--wire-color-on-danger); --modal-contrast: var(--wire-color-on-danger, white);
} }
.wire-modal__trigger, .wire-modal__trigger,
@@ -321,8 +353,35 @@ component Modal {
font-size: 0.85rem; font-size: 0.85rem;
font-weight: 650; font-weight: 650;
cursor: pointer; cursor: pointer;
transition: opacity 150ms ease, transform 150ms ease, box-shadow 150ms ease;
} }
.wire-modal__trigger:hover {
opacity: 0.92;
}
.wire-modal__trigger:active {
transform: scale(0.97);
}
.wire-modal__trigger:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--modal-accent) 40%, transparent);
}
/*
* Hidden by default so the SSR-rendered HTML never paints the layer before
* hydration runs. data-open on the wire-modal root is evaluated and
* serialized to a real true or false string at render time -- a plain
* bind, not the raw-expression data-show directive -- so this selector
* is correct on first paint with zero flash, with no dependency on
* client JS having run yet. The data-show attribute and client directive
* still run after hydration to keep things in sync for state changes,
* but visibility itself is driven by CSS here. visibility (rather than
* display) is used so the open and close transitions below can actually
* animate -- a box that starts at display: none has no prior frame to
* transition from.
*/
.wire-modal__layer { .wire-modal__layer {
position: fixed; position: fixed;
inset: 0; inset: 0;
@@ -331,6 +390,15 @@ component Modal {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 1rem; padding: 1rem;
visibility: hidden;
opacity: 0;
transition: opacity 180ms ease, visibility 0s linear 180ms;
}
.wire-modal[data-open="true"] .wire-modal__layer {
visibility: visible;
opacity: 1;
transition: opacity 180ms ease, visibility 0s linear 0s;
} }
.wire-modal[data-placement="top"] .wire-modal__layer { .wire-modal[data-placement="top"] .wire-modal__layer {
@@ -338,6 +406,12 @@ component Modal {
padding-top: clamp(1rem, 8vh, 5rem); padding-top: clamp(1rem, 8vh, 5rem);
} }
.wire-modal[data-scroll="page"] .wire-modal__layer {
align-items: flex-start;
overflow-y: auto;
padding: 2.5rem 1rem;
}
.wire-modal__backdrop { .wire-modal__backdrop {
position: absolute; position: absolute;
inset: 0; inset: 0;
@@ -370,6 +444,18 @@ component Modal {
0 1px 0 color-mix(in srgb, white 5%, transparent) inset, 0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
0 40px 110px color-mix(in srgb, black 38%, transparent); 0 40px 110px color-mix(in srgb, black 38%, transparent);
overflow: hidden; overflow: hidden;
opacity: 0;
transform: scale(0.96) translateY(10px);
transition: opacity 180ms ease, transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
.wire-modal[data-open="true"] .wire-modal__panel {
opacity: 1;
transform: none;
}
.wire-modal[data-size="xs"] .wire-modal__panel {
width: min(19rem, calc(100vw - 2rem));
} }
.wire-modal[data-size="sm"] .wire-modal__panel { .wire-modal[data-size="sm"] .wire-modal__panel {
@@ -390,6 +476,10 @@ component Modal {
max-height: none; max-height: none;
} }
.wire-modal[data-scroll="page"] .wire-modal__panel {
max-height: none;
}
.wire-modal[data-variant="soft"] .wire-modal__panel { .wire-modal[data-variant="soft"] .wire-modal__panel {
background: background:
linear-gradient(145deg, var(--modal-soft), transparent 68%), linear-gradient(145deg, var(--modal-soft), transparent 68%),
@@ -447,6 +537,7 @@ component Modal {
} }
.wire-modal__heading-copy h2 { .wire-modal__heading-copy h2 {
color: inherit;
font-size: 1.08rem; font-size: 1.08rem;
font-weight: 650; font-weight: 650;
line-height: 1.3; line-height: 1.3;
@@ -462,32 +553,100 @@ component Modal {
color: color-mix(in srgb, currentColor 76%, transparent); color: color-mix(in srgb, currentColor 76%, transparent);
} }
/*
* padding is reset explicitly: an app-level `button { padding: … }` rule
* outranks the browser default, and 1rem of horizontal padding left this
* 2.15rem button with a ~2px content box -- which squeezed the icon to
* 0.4px wide and read as "the close button has no icon".
*/
.wire-modal__close { .wire-modal__close {
appearance: none; appearance: none;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex: 0 0 auto; flex: 0 0 auto;
width: 2.35rem; padding: 0;
height: 2.35rem; width: 2.15rem;
height: 2.15rem;
color: var(--wire-color-text-muted); color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft); background: transparent;
border: 1px solid var(--wire-color-border); border: 1px solid transparent;
border-radius: 0.75rem; border-radius: 9999px;
cursor: pointer; cursor: pointer;
transition: background 150ms ease, color 150ms ease, border-color 150ms ease, transform 150ms ease;
}
/*
* On a solid panel the background is the accent color, so the muted-grey
* default is close to invisible -- the dismiss affordance reads as
* missing rather than subtle. Derive it from the panel contrast color
* instead, and give it a faint ring so it is unmistakably a control.
*/
.wire-modal[data-variant="solid"] .wire-modal__close {
color: color-mix(in srgb, currentColor 82%, transparent);
border-color: color-mix(in srgb, currentColor 35%, transparent);
}
.wire-modal[data-variant="solid"] .wire-modal__close:hover {
color: currentColor;
background: color-mix(in srgb, black 18%, transparent);
border-color: color-mix(in srgb, currentColor 55%, transparent);
}
.wire-modal__close:hover {
color: var(--wire-color-text);
background: var(--wire-color-surface-soft);
} }
.wire-modal__close:hover,
.wire-modal__close:focus-visible { .wire-modal__close:focus-visible {
color: var(--modal-accent); color: var(--modal-accent);
border-color: color-mix(in srgb, var(--modal-accent) 34%, var(--wire-color-border)); border-color: color-mix(in srgb, var(--modal-accent) 45%, transparent);
outline: none; outline: none;
} }
.wire-modal__close:active {
transform: scale(0.92);
}
/* Never let the glyph be shrunk by the flex container. */
.wire-modal__close svg {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
}
/*
* Slot content is authored by the host app, so the app global stylesheet
* styles it too. A bare element selector there (p { color: ... }) beats
* anything the panel merely *inherits*, which is how solid-variant modals
* ended up with muted grey body copy on a saturated accent background --
* unreadable, and worst exactly where contrast matters most (the
* destructive confirm). Setting the color on the body makes the panel
* choice explicit instead of leaving it to inheritance.
*
* NOTE: apostrophes are avoided in .wrn style comments on purpose -- the
* block scanner treats a quote as a string delimiter while it counts
* braces, so a stray one breaks parsing of the whole component.
*/
.wire-modal__body { .wire-modal__body {
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
padding: 1.4rem; padding: 1.4rem;
color: var(--wire-color-text);
}
/*
* :where() keeps this at the specificity of .wire-modal__body alone, so it
* outranks a global element selector but still yields to any class the app
* puts on its own slot content (an error message, a muted caption). A
* plain .wire-modal__body p list would have quietly overridden those.
*/
.wire-modal__body :where(p, li, dd, dt, h1, h2, h3, h4, h5, h6, span, label, code) {
color: inherit;
}
.wire-modal[data-variant="solid"] .wire-modal__body {
color: var(--modal-contrast);
} }
.wire-modal[data-scrollable="true"] .wire-modal__body { .wire-modal[data-scrollable="true"] .wire-modal__body {
@@ -548,6 +707,18 @@ component Modal {
border: 1px solid transparent; border: 1px solid transparent;
} }
/*
* On a solid-variant panel the panel background is also --modal-accent,
* so a plain primary button (same color) has no visible edge against it.
* Darken the fill slightly and add a light border so the button still
* reads as a distinct, clickable pill instead of blending into the panel.
*/
.wire-modal[data-variant="solid"] .wire-modal__button--primary {
background: color-mix(in srgb, black 18%, var(--modal-accent));
border-color: color-mix(in srgb, white 32%, transparent);
box-shadow: 0 1px 0 color-mix(in srgb, white 12%, transparent) inset;
}
.wire-modal__button:disabled { .wire-modal__button:disabled {
opacity: 0.55; opacity: 0.55;
cursor: not-allowed; cursor: not-allowed;
@@ -575,6 +746,7 @@ component Modal {
} }
.wire-modal__panel, .wire-modal__panel,
.wire-modal[data-size="xs"] .wire-modal__panel,
.wire-modal[data-size="sm"] .wire-modal__panel, .wire-modal[data-size="sm"] .wire-modal__panel,
.wire-modal[data-size="lg"] .wire-modal__panel, .wire-modal[data-size="lg"] .wire-modal__panel,
.wire-modal[data-size="xl"] .wire-modal__panel { .wire-modal[data-size="xl"] .wire-modal__panel {
@@ -599,10 +771,17 @@ component Modal {
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.wire-modal__button, .wire-modal__button,
.wire-modal__spinner { .wire-modal__spinner,
.wire-modal__layer,
.wire-modal__panel,
.wire-modal__close {
animation: none; animation: none;
transition: none; transition: none;
} }
.wire-modal__panel {
transform: none;
}
} }
} }
} }
+8 -1
View File
@@ -123,12 +123,13 @@ component Popover {
<section <section
class="wire-popover__panel" class="wire-popover__panel"
data-wrn-anchored="true"
data-show='{open || visible}' data-show='{open || visible}'
role="dialog" role="dialog"
aria-label='{title || triggerLabel}' aria-label='{title || triggerLabel}'
> >
{#if showArrow} {#if showArrow}
<span class="wire-popover__arrow" aria-hidden="true"></span> <span class="wire-popover__arrow" data-wrn-anchor-arrow="true" aria-hidden="true"></span>
{/if} {/if}
{#if title || description || icon || showClose} {#if title || description || icon || showClose}
@@ -471,7 +472,13 @@ component Popover {
color: color-mix(in srgb, currentColor 76%, transparent); color: color-mix(in srgb, currentColor 76%, transparent);
} }
/*
* padding is reset explicitly: an app-level `button { padding: ... }` rule
* outranks the browser default and crushes the icon inside this
* fixed-size button. Same trap as Modal and Drawer.
*/
.wire-popover__close { .wire-popover__close {
padding: 0;
appearance: none; appearance: none;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
-20
View File
@@ -1,20 +0,0 @@
component Table {
outputs {
sort(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
rowClick(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
}
props {
size: string = "default"
color: string = "primary"
caption: string = "Table"
columns: unknown[] = []
rows: unknown[] = []
striped: boolean = true
class: string = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--table {class}"><table><caption>{caption}</caption><thead><tr>{#each columns as column}<th>{column.label}</th>{/each}</tr></thead><tbody>{#each rows as row}<tr>{#each columns as column}<td>{row[column.key]}</td>{/each}</tr>{/each}</tbody></table><slot /></div>
}
}
+2 -1
View File
@@ -97,11 +97,12 @@ id: string = ""
<span <span
id='{id}' id='{id}'
class="wire-tooltip__content" class="wire-tooltip__content"
data-wrn-anchored="true"
data-show='{open || visible}' data-show='{open || visible}'
role="tooltip" role="tooltip"
> >
{#if showArrow} {#if showArrow}
<span class="wire-tooltip__arrow" aria-hidden="true"></span> <span class="wire-tooltip__arrow" data-wrn-anchor-arrow="true" aria-hidden="true"></span>
{/if} {/if}
{#if title} {#if title}
+6 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/ui", "name": "@wrnexus/ui",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "@wrnexus/ui — part of the WrNexus framework.", "description": "@wrnexus/ui — part of the WrNexus framework.",
"license": "MIT", "license": "MIT",
@@ -35,6 +35,10 @@
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"import": "./dist/index.js" "import": "./dist/index.js"
}, },
"./registry": {
"types": "./dist/registry.d.ts",
"import": "./dist/registry.js"
},
"./components/*": "./components/*", "./components/*": "./components/*",
"./component-catalog.json": "./component-catalog.json", "./component-catalog.json": "./component-catalog.json",
"./component-migrations.json": "./component-migrations.json", "./component-migrations.json": "./component-migrations.json",
@@ -42,7 +46,7 @@
"./ui.css": "./ui.css" "./ui.css": "./ui.css"
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4" "@wrnexus/core": "^0.8.5"
}, },
"files": [ "files": [
"dist", "dist",
-22
View File
@@ -8037,28 +8037,6 @@
cursor: pointer; cursor: pointer;
} }
.wire-next--table {
width: 100%;
overflow: auto;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
}
.wire-next--table table {
width: 100%;
border-collapse: collapse;
}
.wire-next--table caption,
.wire-next--table th,
.wire-next--table td {
padding: 0.75rem;
border-bottom: 1px solid var(--wire-color-border);
text-align: left;
}
.wire-next--table caption,
.wire-next--table th {
font-weight: 800;
}
@keyframes wire-shimmer { @keyframes wire-shimmer {
to { to {
background-position: -200% 0; background-position: -200% 0;
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/uploader", "name": "@wrnexus/uploader",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "Secure upload drivers, policies, client runtime, helper functions, and reusable upload components.", "description": "Secure upload drivers, policies, client runtime, helper functions, and reusable upload components.",
"license": "MIT", "license": "MIT",
@@ -42,9 +42,9 @@
"./components/*": "./components/*" "./components/*": "./components/*"
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/plugin": "^0.8.4", "@wrnexus/plugin": "^0.8.5",
"@wrnexus/ui": "^0.8.4" "@wrnexus/ui": "^0.8.5"
}, },
"wrnexus": { "wrnexus": {
"plugin": { "plugin": {
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "@wrnexus/validation", "name": "@wrnexus/validation",
"version": "0.8.4", "version": "0.8.5",
"type": "module", "type": "module",
"description": "Shared server/browser schemas, validation helpers, form runtime, and reusable error components.", "description": "Shared server/browser schemas, validation helpers, form runtime, and reusable error components.",
"license": "MIT", "license": "MIT",
@@ -42,9 +42,9 @@
"./components/*": "./components/*" "./components/*": "./components/*"
}, },
"dependencies": { "dependencies": {
"@wrnexus/core": "^0.8.4", "@wrnexus/core": "^0.8.5",
"@wrnexus/plugin": "^0.8.4", "@wrnexus/plugin": "^0.8.5",
"@wrnexus/ui": "^0.8.4" "@wrnexus/ui": "^0.8.5"
}, },
"wrnexus": { "wrnexus": {
"plugin": { "plugin": {