chore(release): stage 0.8.5 package tarballs
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -149,3 +149,156 @@ app.put(
|
||||
- **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`.
|
||||
- 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);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -34,8 +34,16 @@
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"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": [
|
||||
"dist",
|
||||
"README.md"
|
||||
|
||||
+18
-17
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -44,22 +44,23 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/router": "^0.8.4",
|
||||
"@wrnexus/csr": "^0.8.4",
|
||||
"@wrnexus/compiler": "^0.8.4",
|
||||
"@wrnexus/styles": "^0.8.4",
|
||||
"@wrnexus/dev-server": "^0.8.4",
|
||||
"@wrnexus/ui": "^0.8.4",
|
||||
"@wrnexus/validation": "^0.8.4",
|
||||
"@wrnexus/i18n": "^0.8.4",
|
||||
"@wrnexus/mcp": "^0.8.4",
|
||||
"@wrnexus/playground": "^0.8.4",
|
||||
"@wrnexus/db": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4",
|
||||
"@wrnexus/syntax": "^0.8.4",
|
||||
"@wrnexus/typecheck": "^0.8.4",
|
||||
"@wrnexus/security": "^0.8.4",
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/router": "^0.8.5",
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/styles": "^0.8.5",
|
||||
"@wrnexus/dev-server": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5",
|
||||
"@wrnexus/i18n": "^0.8.5",
|
||||
"@wrnexus/mcp": "^0.8.5",
|
||||
"@wrnexus/playground": "^0.8.5",
|
||||
"@wrnexus/db": "^0.8.5",
|
||||
"@wrnexus/authz": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/syntax": "^0.8.5",
|
||||
"@wrnexus/typecheck": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5",
|
||||
"selfsigned": "^5.5.0"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,10 +37,10 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/csr": "^0.8.4",
|
||||
"@wrnexus/syntax": "^0.8.4",
|
||||
"@wrnexus/store": "^0.8.4",
|
||||
"@wrnexus/validation": "^0.8.4"
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/syntax": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -41,25 +41,27 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/dev-toolbar": "^0.8.4",
|
||||
"@wrnexus/router": "^0.8.4",
|
||||
"@wrnexus/ssr": "^0.8.4",
|
||||
"@wrnexus/csr": "^0.8.4",
|
||||
"@wrnexus/compiler": "^0.8.4",
|
||||
"@wrnexus/styles": "^0.8.4",
|
||||
"@wrnexus/ui": "^0.8.4",
|
||||
"@wrnexus/validation": "^0.8.4",
|
||||
"@wrnexus/i18n": "^0.8.4",
|
||||
"@wrnexus/db": "^0.8.4",
|
||||
"@wrnexus/pubsub": "^0.8.4",
|
||||
"@wrnexus/uploader": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4",
|
||||
"@wrnexus/store": "^0.8.4",
|
||||
"@wrnexus/security": "^0.8.4",
|
||||
"@wrnexus/observability": "^0.8.4",
|
||||
"@wrnexus/cache": "^0.8.4",
|
||||
"@wrnexus/pwa": "^0.8.4"
|
||||
"@wrnexus/authz": "^0.8.5",
|
||||
"@wrnexus/rpc": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/dev-toolbar": "^0.8.5",
|
||||
"@wrnexus/router": "^0.8.5",
|
||||
"@wrnexus/ssr": "^0.8.5",
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/styles": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5",
|
||||
"@wrnexus/i18n": "^0.8.5",
|
||||
"@wrnexus/db": "^0.8.5",
|
||||
"@wrnexus/pubsub": "^0.8.5",
|
||||
"@wrnexus/uploader": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5",
|
||||
"@wrnexus/observability": "^0.8.5",
|
||||
"@wrnexus/cache": "^0.8.5",
|
||||
"@wrnexus/pwa": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Locale loading, fallback resolution, SSR/browser translations, formatters, and WRNexusJS language components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4",
|
||||
"@wrnexus/ui": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.8.4"
|
||||
"@wrnexus/native": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/jwt": "^0.8.4"
|
||||
"@wrnexus/jwt": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/plugin — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -49,7 +49,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "^0.8.4"
|
||||
"@wrnexus/syntax": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,8 +37,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.8.4",
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -45,9 +45,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/store": "^0.8.4",
|
||||
"@wrnexus/security": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,9 +37,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.8.4",
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4"
|
||||
"@wrnexus/uploader": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/syntax — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+16
-16
@@ -359,6 +359,15 @@ Reusable preference switcher component.
|
||||
- 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[])`
|
||||
|
||||
### 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
|
||||
|
||||
### MetricCard
|
||||
@@ -554,15 +563,6 @@ Theme-aware, responsive data map component.
|
||||
- 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)`
|
||||
|
||||
### 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
|
||||
|
||||
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.
|
||||
|
||||
- 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`
|
||||
- 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.
|
||||
|
||||
- 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`
|
||||
- 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
|
||||
|
||||
### Table
|
||||
### DataTable
|
||||
|
||||
Theme-aware, responsive table component.
|
||||
Sortable, filterable, paginated data table with row selection.
|
||||
|
||||
- Mount: `data-component="Table"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `caption: string = "Table"`, `columns: unknown[] = []`, `rows: unknown[] = []`, `striped: boolean = true`, `class: string = ""`
|
||||
- Mount: `data-component="DataTable"`
|
||||
- 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`
|
||||
- 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 })`
|
||||
|
||||
@@ -168,8 +168,8 @@
|
||||
},
|
||||
{
|
||||
"name": "DataTable",
|
||||
"category": "integrations",
|
||||
"purpose": "Theme-aware, responsive data table component."
|
||||
"category": "tables",
|
||||
"purpose": "Sortable, filterable, paginated data table with row selection."
|
||||
},
|
||||
{
|
||||
"name": "DatePicker",
|
||||
@@ -471,11 +471,6 @@
|
||||
"category": "forms",
|
||||
"purpose": "Theme-aware, responsive switch component."
|
||||
},
|
||||
{
|
||||
"name": "Table",
|
||||
"category": "tables",
|
||||
"purpose": "Theme-aware, responsive table component."
|
||||
},
|
||||
{
|
||||
"name": "Tabs",
|
||||
"category": "navigation",
|
||||
@@ -511,6 +506,11 @@
|
||||
"category": "integrations",
|
||||
"purpose": "Theme-aware, responsive toast notifications component."
|
||||
},
|
||||
{
|
||||
"name": "Toaster",
|
||||
"category": "core",
|
||||
"purpose": "Reusable toaster component."
|
||||
},
|
||||
{
|
||||
"name": "ToggleCount",
|
||||
"category": "advanced-forms",
|
||||
|
||||
@@ -4569,16 +4569,9 @@
|
||||
{
|
||||
"name": "DataTable",
|
||||
"mount": "DataTable",
|
||||
"category": "integrations",
|
||||
"purpose": "Theme-aware, responsive data table component.",
|
||||
"category": "tables",
|
||||
"purpose": "Sortable, filterable, paginated data table with row selection.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -4587,10 +4580,10 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "caption",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Data Table\"",
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -4607,6 +4600,111 @@
|
||||
"default": "[]",
|
||||
"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",
|
||||
"type": "boolean",
|
||||
@@ -4614,6 +4712,62 @@
|
||||
"default": "true",
|
||||
"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",
|
||||
"type": "string",
|
||||
@@ -4626,26 +4780,47 @@
|
||||
"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"
|
||||
"payloadType": "{ key: string; direction: string }"
|
||||
},
|
||||
{
|
||||
"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": "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": "search",
|
||||
"payloadType": "{ query: string }"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
@@ -5220,6 +5395,13 @@
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "260",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "overlay",
|
||||
"type": "boolean",
|
||||
@@ -8736,6 +8918,13 @@
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "scrollBehavior",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"inside\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -12009,80 +12198,6 @@
|
||||
"events": ["input", "change", "focus", "blur"],
|
||||
"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",
|
||||
"mount": "Tabs",
|
||||
@@ -12893,6 +13008,136 @@
|
||||
"events": ["add", "dismiss", "clear", "action"],
|
||||
"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",
|
||||
"mount": "ToggleCount",
|
||||
|
||||
@@ -45,8 +45,12 @@ component ContextMenu {
|
||||
sourceEvent.preventDefault()
|
||||
}
|
||||
if (placement === "pointer" && sourceEvent) {
|
||||
positionX = Math.max(12, Math.min(sourceEvent.clientX || 12, window.innerWidth - 340))
|
||||
positionY = Math.max(12, Math.min(sourceEvent.clientY || 12, window.innerHeight - 420))
|
||||
// Place the menu at the pointer and let the anchored clamp in the
|
||||
// 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
|
||||
output.open({
|
||||
@@ -161,6 +165,7 @@ component ContextMenu {
|
||||
|
||||
<div
|
||||
class="wire-context-menu__panel"
|
||||
data-wrn-anchored="true"
|
||||
data-show='{open || visible}'
|
||||
role="menu"
|
||||
aria-label='{label}'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@ open: boolean = false
|
||||
showClose: boolean = true
|
||||
closeOnBackdrop: boolean = true
|
||||
closeOnEscape: boolean = true
|
||||
// Open/close animation length in ms. 0 disables the animation entirely.
|
||||
duration: number = 260
|
||||
overlay: boolean = true
|
||||
scrollable: boolean = true
|
||||
triggerLabel: string = ""
|
||||
@@ -79,7 +81,9 @@ open: boolean = false
|
||||
data-overlay='{overlay ? "true" : "false"}'
|
||||
data-scrollable='{scrollable ? "true" : "false"}'
|
||||
class='wire-drawer {class}'
|
||||
style='--drawer-duration: {duration}ms'
|
||||
>
|
||||
|
||||
{#if triggerLabel}
|
||||
<button
|
||||
type="button"
|
||||
@@ -101,7 +105,6 @@ open: boolean = false
|
||||
|
||||
<div
|
||||
class="wire-drawer__layer"
|
||||
data-show='{open || visible}'
|
||||
role="presentation"
|
||||
@keydown='handleKeydown(event)'
|
||||
>
|
||||
@@ -218,12 +221,32 @@ open: boolean = false
|
||||
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 {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1200;
|
||||
display: flex;
|
||||
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 {
|
||||
@@ -262,6 +285,38 @@ open: boolean = false
|
||||
box-shadow: -28px 0 80px color-mix(in srgb, black 28%, transparent);
|
||||
pointer-events: auto;
|
||||
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 {
|
||||
@@ -395,12 +450,19 @@ open: boolean = false
|
||||
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 {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
width: 2.35rem;
|
||||
height: 2.35rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
@@ -417,10 +479,30 @@ open: boolean = false
|
||||
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 {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
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 {
|
||||
|
||||
@@ -171,6 +171,7 @@ items: unknown[] = []
|
||||
|
||||
<div
|
||||
class="wire-dropdown__panel"
|
||||
data-wrn-anchored="true"
|
||||
data-show='{open || visible}'
|
||||
role="menu"
|
||||
aria-label='{menuLabel}'
|
||||
|
||||
@@ -35,6 +35,7 @@ component Modal {
|
||||
triggerLabel: string = ""
|
||||
triggerIcon: string = ""
|
||||
scrollable: boolean = true
|
||||
scrollBehavior: string = "inside"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -50,6 +51,16 @@ component Modal {
|
||||
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) {
|
||||
visible = false
|
||||
output.close({
|
||||
@@ -87,21 +98,23 @@ component Modal {
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Modal"
|
||||
data-open='{open || visible ? "true" : "false"}'
|
||||
data-open='{isOpen() ? "true" : "false"}'
|
||||
data-size='{size}'
|
||||
data-placement='{placement}'
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
data-scrollable='{scrollable ? "true" : "false"}'
|
||||
data-scroll='{scrollBehavior}'
|
||||
data-destructive='{destructive ? "true" : "false"}'
|
||||
class='wire-modal {class}'
|
||||
@wrnexus:modal:close='hideModal("api", event)'
|
||||
>
|
||||
{#if triggerLabel}
|
||||
<button
|
||||
type="button"
|
||||
class="wire-modal__trigger"
|
||||
aria-haspopup="dialog"
|
||||
aria-expanded='{open || visible ? "true" : "false"}'
|
||||
aria-expanded='{isOpen() ? "true" : "false"}'
|
||||
@click='showModal(event)'
|
||||
>
|
||||
{#if triggerIcon}
|
||||
@@ -127,7 +140,7 @@ component Modal {
|
||||
|
||||
<div
|
||||
class="wire-modal__layer"
|
||||
data-show='{open || visible}'
|
||||
data-show="isOpen()"
|
||||
role="presentation"
|
||||
@keydown='handleKeydown(event)'
|
||||
>
|
||||
@@ -183,11 +196,20 @@ component Modal {
|
||||
aria-label='{closeLabel}'
|
||||
@click='hideModal("close-button", event)'
|
||||
>
|
||||
<span
|
||||
class="icon-[lucide--x]"
|
||||
<svg
|
||||
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"
|
||||
>
|
||||
</span>
|
||||
<path d="M18 6 6 18" />
|
||||
<path d="M6 6 18 18" />
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</header>
|
||||
@@ -277,16 +299,26 @@ component Modal {
|
||||
--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"] {
|
||||
--modal-accent: var(--wire-color-info);
|
||||
--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"] {
|
||||
--modal-accent: var(--wire-color-success);
|
||||
--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"] {
|
||||
@@ -299,7 +331,7 @@ component Modal {
|
||||
.wire-modal[data-destructive="true"] {
|
||||
--modal-accent: var(--wire-color-danger);
|
||||
--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,
|
||||
@@ -321,8 +353,35 @@ component Modal {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 650;
|
||||
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 {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -331,6 +390,15 @@ component Modal {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
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 {
|
||||
@@ -338,6 +406,12 @@ component Modal {
|
||||
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 {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -370,6 +444,18 @@ component Modal {
|
||||
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
|
||||
0 40px 110px color-mix(in srgb, black 38%, transparent);
|
||||
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 {
|
||||
@@ -390,6 +476,10 @@ component Modal {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.wire-modal[data-scroll="page"] .wire-modal__panel {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.wire-modal[data-variant="soft"] .wire-modal__panel {
|
||||
background:
|
||||
linear-gradient(145deg, var(--modal-soft), transparent 68%),
|
||||
@@ -447,6 +537,7 @@ component Modal {
|
||||
}
|
||||
|
||||
.wire-modal__heading-copy h2 {
|
||||
color: inherit;
|
||||
font-size: 1.08rem;
|
||||
font-weight: 650;
|
||||
line-height: 1.3;
|
||||
@@ -462,32 +553,100 @@ component Modal {
|
||||
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 {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 2.35rem;
|
||||
height: 2.35rem;
|
||||
padding: 0;
|
||||
width: 2.15rem;
|
||||
height: 2.15rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
background: var(--wire-color-surface-soft);
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: 0.75rem;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 9999px;
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
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 {
|
||||
@@ -548,6 +707,18 @@ component Modal {
|
||||
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 {
|
||||
opacity: 0.55;
|
||||
cursor: not-allowed;
|
||||
@@ -575,6 +746,7 @@ component Modal {
|
||||
}
|
||||
|
||||
.wire-modal__panel,
|
||||
.wire-modal[data-size="xs"] .wire-modal__panel,
|
||||
.wire-modal[data-size="sm"] .wire-modal__panel,
|
||||
.wire-modal[data-size="lg"] .wire-modal__panel,
|
||||
.wire-modal[data-size="xl"] .wire-modal__panel {
|
||||
@@ -599,10 +771,17 @@ component Modal {
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wire-modal__button,
|
||||
.wire-modal__spinner {
|
||||
.wire-modal__spinner,
|
||||
.wire-modal__layer,
|
||||
.wire-modal__panel,
|
||||
.wire-modal__close {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.wire-modal__panel {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,12 +123,13 @@ component Popover {
|
||||
|
||||
<section
|
||||
class="wire-popover__panel"
|
||||
data-wrn-anchored="true"
|
||||
data-show='{open || visible}'
|
||||
role="dialog"
|
||||
aria-label='{title || triggerLabel}'
|
||||
>
|
||||
{#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 title || description || icon || showClose}
|
||||
@@ -471,7 +472,13 @@ component Popover {
|
||||
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 {
|
||||
padding: 0;
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,12 @@ id: string = ""
|
||||
<span
|
||||
id='{id}'
|
||||
class="wire-tooltip__content"
|
||||
data-wrn-anchored="true"
|
||||
data-show='{open || visible}'
|
||||
role="tooltip"
|
||||
>
|
||||
{#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 title}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -35,6 +35,10 @@
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
"./registry": {
|
||||
"types": "./dist/registry.d.ts",
|
||||
"import": "./dist/registry.js"
|
||||
},
|
||||
"./components/*": "./components/*",
|
||||
"./component-catalog.json": "./component-catalog.json",
|
||||
"./component-migrations.json": "./component-migrations.json",
|
||||
@@ -42,7 +46,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -8037,28 +8037,6 @@
|
||||
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 {
|
||||
to {
|
||||
background-position: -200% 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Secure upload drivers, policies, client runtime, helper functions, and reusable upload components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4",
|
||||
"@wrnexus/ui": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"type": "module",
|
||||
"description": "Shared server/browser schemas, validation helpers, form runtime, and reusable error components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.4",
|
||||
"@wrnexus/plugin": "^0.8.4",
|
||||
"@wrnexus/ui": "^0.8.4"
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
Reference in New Issue
Block a user