refactor: migrate legacy wire namespace to wrn
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-12 18:51:15 +05:30
parent ec23dd4c93
commit 2c960fc1dc
488 changed files with 27306 additions and 19063 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ dist/
**/.wrnexus/**
**/.wrnexus-*/**
.publish/
**/.wirefw/
**/.wrnfw/
coverage/
bun.lock
bun.lockb
@@ -14,7 +14,7 @@ bun.lockb
**/*.gen.ts
**/*.generated.d.ts
# Bundled .wire compiler for the VS Code extension (generated)
# Bundled .wrn compiler for the VS Code extension (generated)
editors/vscode/src/compiler.cjs
editors/vscode/src/language-server.cjs
editors/vscode/src/extension.bundle.cjs
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
"license": "MIT",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/authz — part of the WrNexus framework.",
"license": "MIT",
@@ -41,8 +41,8 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8",
"@wrnexus/db": "^0.8.8"
"@wrnexus/core": "^0.8.9",
"@wrnexus/db": "^0.8.9"
},
"files": [
"dist",
+3 -3
View File
@@ -79,7 +79,7 @@ Every command accepts an optional `[app-dir]` (defaults to `.`). Commands that r
| `wrnexus generate docker` | Scaffold `Dockerfile`, `.dockerignore`, and `docker-compose.yml`. |
| `wrnexus generate mobile` | Scaffold a Capacitor shell for iOS and Android. |
| `wrnexus mobile add <package...>` | Install Capacitor plugins and sync native projects. |
| `wrnexus eject <name...>` | Copy Wire UI component `.wrn` sources into `app/components/`. |
| `wrnexus eject <name...>` | Copy WrNexus UI component `.wrn` sources into `app/components/`. |
| `wrnexus db <cmd>` | Database migrations and tooling (see [db](#wrnexus-db)). |
| `wrnexus test [app-dir] [--watch]` | Run the app's tests via `bun test` (defaults to the `test` profile). |
| `wrnexus profiles [app-dir]` | List config profiles and their `.env` files, marking the active one. |
@@ -179,7 +179,7 @@ WrNexus response pages.
### `wrnexus eject`
Copies a Wire UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app.
Copies a WrNexus UI component's `.wrn` source out of `@wrnexus/ui` into `app/components/`, so the app owns and can edit it (the app copy shadows the library one by name). Run with no names to list available components. It skips components that already exist in the app.
```bash
wrnexus eject button card modal
@@ -322,5 +322,5 @@ export default config;
## Requirements / Notes
- **Bun-only.** The CLI runs on Bun, spawns the Bun binary for the dev child and `bun test`, and the production build uses `Bun.build`. Node is not supported.
- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn``.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable Wire UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`.
- Orchestrates the rest of the framework: `@wrnexus/dev-server` (dev/prod server + gateway), `@wrnexus/router` (route + typed-routes codegen), `@wrnexus/compiler` (`.wrn``.ts`), `@wrnexus/db` (migrations, typed queries), `@wrnexus/styles` (config, profiles, `.env`, themes, styles), `@wrnexus/ui` (ejectable WrNexus UI components), `@wrnexus/validation`, `@wrnexus/csr`, and `@wrnexus/i18n`.
- Reads `wrnexus.config.ts` for `db` / `databases`, `theme`, `styles`, `seo`, `security`, `i18n`, and `profiles`, and `wrnexus.workspace.ts` for the gateway.
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.8.8",
"version": "0.8.17",
"type": "module",
"description": "@wrnexus/cli — part of the WrNexus framework.",
"license": "MIT",
@@ -44,19 +44,19 @@
"wrnexus": "./dist/index.js"
},
"dependencies": {
"@wrnexus/core": "^0.8.8",
"@wrnexus/router": "^0.8.8",
"@wrnexus/csr": "^0.8.8",
"@wrnexus/compiler": "^0.8.8",
"@wrnexus/styles": "^0.8.8",
"@wrnexus/dev-server": "^0.8.8",
"@wrnexus/ui": "^0.8.8",
"@wrnexus/validation": "^0.8.8",
"@wrnexus/i18n": "^0.8.8",
"@wrnexus/core": "^0.8.9",
"@wrnexus/router": "^0.8.9",
"@wrnexus/csr": "^0.8.16",
"@wrnexus/compiler": "^0.8.9",
"@wrnexus/styles": "^0.8.10",
"@wrnexus/dev-server": "^0.8.16",
"@wrnexus/ui": "^0.8.12",
"@wrnexus/validation": "^0.8.9",
"@wrnexus/i18n": "^0.8.9",
"@wrnexus/mcp": "^0.8.8",
"@wrnexus/playground": "^0.8.8",
"@wrnexus/db": "^0.8.8",
"@wrnexus/authz": "^0.8.8",
"@wrnexus/db": "^0.8.9",
"@wrnexus/authz": "^0.8.9",
"@wrnexus/plugin": "^0.8.8",
"@wrnexus/syntax": "^0.8.8",
"@wrnexus/typecheck": "^0.8.8",
+8 -8
View File
@@ -69,7 +69,7 @@ bun add @wrnexus/compiler
All exports come from the package root (`@wrnexus/compiler`).
### `compileWireFile(source: string): string`
### `compileWrnFile(source: string): string`
Compile `.wrn` source to a TypeScript module string. Throws `ParseError` on invalid input. The output is prefixed with a `// compiled from .wrn` comment.
@@ -115,10 +115,10 @@ class Lexer {
### Errors
| Class | Thrown by | Meaning |
| ------------ | ------------------------------------------------- | --------------------------------------------------------------- |
| `ParseError` | `parse`, `compile`, `compileWireFile`, `generate` | Invalid `.wrn` grammar or (rewrapped) lex failure. |
| `LexError` | `Lexer` | Unexpected character / unterminated string / unbalanced braces. |
| Class | Thrown by | Meaning |
| ------------ | ------------------------------------------------ | --------------------------------------------------------------- |
| `ParseError` | `parse`, `compile`, `compileWrnFile`, `generate` | Invalid `.wrn` grammar or (rewrapped) lex failure. |
| `LexError` | `Lexer` | Unexpected character / unterminated string / unbalanced braces. |
### AST types
@@ -143,9 +143,9 @@ Exported type-only symbols describing the parsed tree:
Compile a page:
```ts
import { compileWireFile } from "@wrnexus/compiler";
import { compileWrnFile } from "@wrnexus/compiler";
const ts = compileWireFile(`
const ts = compileWrnFile(`
page Home {
state count = 0
seo { title = "Home" description = "Welcome" }
@@ -214,4 +214,4 @@ A file opens with `page <Name>` or `component <Name>` followed by a `{ ... }` bo
## Requirements / Notes
- Pure TypeScript with no runtime dependencies; runs under **Bun** as part of the WrNexus toolchain (Node is not supported).
- Generated modules target WrNexus runtime primitives (`data-scope`, `data-text`, `data-on-*`, `data-for`, `data-component`, `__wrnexus*`/`__wire*` helpers) — consume the output within a WrNexus app, e.g. via `@wrnexus/core`'s dev loader.
- Generated modules target WrNexus runtime primitives (`data-scope`, `data-text`, `data-on-*`, `data-for`, `data-component`, `__wrnexus*`/`__wrn*` helpers) — consume the output within a WrNexus app, e.g. via `@wrnexus/core`'s dev loader.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/compiler — part of the WrNexus framework.",
"license": "MIT",
@@ -37,10 +37,10 @@
}
},
"dependencies": {
"@wrnexus/csr": "^0.8.8",
"@wrnexus/csr": "^0.8.16",
"@wrnexus/syntax": "^0.8.8",
"@wrnexus/store": "^0.8.8",
"@wrnexus/validation": "^0.8.8"
"@wrnexus/validation": "^0.8.9"
},
"files": [
"dist",
+8 -8
View File
@@ -37,7 +37,7 @@ The `Context` (`ctx`) is the single value passed to middleware and handlers.
| `Context` | type | Per-request object: `req`, `url`, `lang`, `t`, `params`, `locals`, `user?`, `ip?`, `cookies`, `session`, `localStorage`. |
| `Next` | type | `() => Promise<Response> \| Response` — invokes the next middleware/handler. |
| `Middleware` | type | `(ctx, next) => Promise<Response> \| Response`. Return `next()` to continue, or a `Response` to short-circuit. |
| `createContext(req, url)` | fn | Build a fresh `Context` for an incoming request (wires up cookies, session, localStorage snapshot). |
| `createContext(req, url)` | fn | Build a fresh `Context` for an incoming request (connects up cookies, session, localStorage snapshot). |
| `withContextHeaders(ctx, res)` | fn | Apply accumulated headers (e.g. `Set-Cookie`) from the context onto a response. |
| `PageComponent` | type | `(ctx) => string \| Promise<string>` — a page module's default export. |
| `PageMeta` / `SeoConfig` | type | `<head>` metadata: `title`, `description`, `canonical`, `robots`, `image`, `twitterCard`, `themeColor`, … |
@@ -70,15 +70,15 @@ cookie-backed `SessionStore`.
### CSRF — `@wrnexus/core`
Double-submit cookie pattern: a readable `wire-csrf` cookie is echoed in an
Double-submit cookie pattern: a readable `wrn-csrf` cookie is echoed in an
`x-csrf-token` header on unsafe requests.
| Export | Signature | Notes |
| ----------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `csrfToken(ctx)` | `(Context) => string` | Ensures the CSRF cookie exists and returns its token. |
| `verifyCsrf(ctx)` | `(Context) => boolean` | Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/`ctx.locals._csrf` must match the cookie (constant-time). |
| `csrfProtection()` | `() => Middleware` | 403s unsafe requests with a missing/mismatched token. |
| `CSRF_COOKIE` / `CSRF_HEADER` | `"wire-csrf"` / `"x-csrf-token"` | Cookie & header names. |
| Export | Signature | Notes |
| ----------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `csrfToken(ctx)` | `(Context) => string` | Ensures the CSRF cookie exists and returns its token. |
| `verifyCsrf(ctx)` | `(Context) => boolean` | Safe methods (GET/HEAD/OPTIONS) pass; otherwise header/`ctx.locals._csrf` must match the cookie (constant-time). |
| `csrfProtection()` | `() => Middleware` | 403s unsafe requests with a missing/mismatched token. |
| `CSRF_COOKIE` / `CSRF_HEADER` | `"wrn-csrf"` / `"x-csrf-token"` | Cookie & header names. |
### Rate limiting — `@wrnexus/core`
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/core — part of the WrNexus framework.",
"license": "MIT",
+4 -4
View File
@@ -111,11 +111,11 @@ Intercepts same-origin `<a>` clicks, fetches the target page, and swaps the `#ap
Connects to `/realtime/<name>` over WebSocket (`ws`/`wss` chosen from `location.protocol`). Two usage modes.
Programmatic API via `window.wire`:
Programmatic API via `window.wrn`:
```ts
wire.room(name): Room // open (or reuse) a room connection
wire.bindRooms(root?) // (re)bind declarative [data-room] containers
wrn.room(name): Room // open (or reuse) a room connection
wrn.bindRooms(root?) // (re)bind declarative [data-room] containers
interface Room {
name: string;
@@ -211,7 +211,7 @@ A realtime chat, fully declarative:
Or drive a room from code:
```ts
const room = wire.room("lobby");
const room = wrn.room("lobby");
room.on("chat", (msg) => console.log(msg.user, msg.text));
room.send({ type: "chat", user: "ada", text: "hi" });
```
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.8.8",
"version": "0.8.16",
"type": "module",
"description": "@wrnexus/csr — part of the WrNexus framework.",
"license": "MIT",
@@ -37,7 +37,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8"
"@wrnexus/core": "^0.8.9"
},
"files": [
"dist",
+2 -2
View File
@@ -128,7 +128,7 @@ const events = await getDb("analytics").all("SELECT * FROM hits");
Migrations are `.sql` files (in e.g. `app/db/migrations`), each split into
`-- +up` and `-- +down` sections. A file with no markers is treated entirely as
`up`. Applied names are recorded in a `_wire_migrations` table so each runs once.
`up`. Applied names are recorded in a `_wrn_migrations` table so each runs once.
- `parseMigration(name, content)``Migration` (`{ name, up, down }`).
- `loadMigrations(dir)` — parse all `.sql` files, sorted by filename.
@@ -249,7 +249,7 @@ SQL driver — use `@wrnexus/db/mongo` directly.
- **Bun-only.** Uses `bun:sqlite` (SQLite adapter + session store) and `Bun.SQL`
(Postgres/MySQL). Migrations/scaffolding use `node:fs`/`node:path`.
- Works with `@wrnexus/core``sqliteSessionStore` implements its
`SessionBackend`; `getDb`/`setDb` are wired by the WrNexus runtime from
`SessionBackend`; `getDb`/`setDb` are connected by the WrNexus runtime from
`wrnexus.config.ts`.
- The `mongodb` npm package is an optional, lazily-imported peer — install it
only if you use `@wrnexus/db/mongo`. The core package stays dependency-free.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.",
"license": "MIT",
+12 -12
View File
@@ -6,7 +6,7 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr
## Overview
This package is the server runtime that powers a WrNexus app in both development and production. A single **request runtime** (`createHandlers`) owns HTTP/WebSocket dispatch and SSR document assembly; it knows nothing about _how_ modules and assets are produced, so the dev and prod entry points wire in different backends: dev uses dynamic module loading plus on-the-fly bundling and injects a live-reload client; prod uses a static, pre-built manifest with cache-immutable assets. The package also ships a multi-app **gateway** (route several apps by `Host` header behind one port) and a portable `node:http` adapter for WinterCG hosts. It is entirely server-side and Bun-native (`Bun.serve`, `Bun.file`, `Bun.gzipSync`).
This package is the server runtime that powers a WrNexus app in both development and production. A single **request runtime** (`createHandlers`) owns HTTP/WebSocket dispatch and SSR document assembly; it knows nothing about _how_ modules and assets are produced, so the dev and prod entry points wrn in different backends: dev uses dynamic module loading plus on-the-fly bundling and injects a live-reload client; prod uses a static, pre-built manifest with cache-immutable assets. The package also ships a multi-app **gateway** (route several apps by `Host` header behind one port) and a portable `node:http` adapter for WinterCG hosts. It is entirely server-side and Bun-native (`Bun.serve`, `Bun.file`, `Bun.gzipSync`).
## Installation
@@ -21,16 +21,16 @@ bun add @wrnexus/dev-server
### Main entry (`@wrnexus/dev-server`)
| Export | Kind | Purpose |
| --------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `startServer(opts: ServeOptions)` | `Promise<RunningServer>` | Start the dev server on `Bun.serve`: builds the router, connects/migrates databases, wires assets + HMR, and starts the file watcher. |
| `createHandlers(deps: RuntimeDeps)` | `Handlers` | The shared request runtime (fetch + websocket handlers). Re-exported from `runtime.ts`. |
| `createProductionServer(manifest, opts)` | `Bun.Server` | Start the production server from a precompiled manifest. |
| `createProductionHandlers(manifest, opts)` | `Handlers` | Build the portable prod fetch/websocket handlers with no server bound (the deployment-adapter seam). |
| `startGateway(opts: GatewayOptions)` | `Promise<RunningGateway>` | Boot multiple apps as child processes and route by `Host`. |
| `toRequest`, `writeResponse`, `nodeListener`, `serveNode` | functions | `node:http` ↔ WinterCG `Request`/`Response` adapter. |
| `RESTART_EXIT_CODE` | `number` (`97`) | Exit code the dev child uses to ask the supervisor for a fresh process. |
| `STYLES_HREF`, `HMR_CLIENT_JS` | constants | The global stylesheet URL and the inline HMR client script. |
| Export | Kind | Purpose |
| --------------------------------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `startServer(opts: ServeOptions)` | `Promise<RunningServer>` | Start the dev server on `Bun.serve`: builds the router, connects/migrates databases, connects assets + HMR, and starts the file watcher. |
| `createHandlers(deps: RuntimeDeps)` | `Handlers` | The shared request runtime (fetch + websocket handlers). Re-exported from `runtime.ts`. |
| `createProductionServer(manifest, opts)` | `Bun.Server` | Start the production server from a precompiled manifest. |
| `createProductionHandlers(manifest, opts)` | `Handlers` | Build the portable prod fetch/websocket handlers with no server bound (the deployment-adapter seam). |
| `startGateway(opts: GatewayOptions)` | `Promise<RunningGateway>` | Boot multiple apps as child processes and route by `Host`. |
| `toRequest`, `writeResponse`, `nodeListener`, `serveNode` | functions | `node:http` ↔ WinterCG `Request`/`Response` adapter. |
| `RESTART_EXIT_CODE` | `number` (`97`) | Exit code the dev child uses to ask the supervisor for a fresh process. |
| `STYLES_HREF`, `HMR_CLIENT_JS` | constants | The global stylesheet URL and the inline HMR client script. |
Exported types: `ServeOptions`, `RunningServer`, `RuntimeDeps`, `AssetServer`, `WsData`, `GatewayApp`, `GatewayOptions`, `GatewayAuth`, `GatewaySecurity`, `RunningGateway`, `FetchHandler`.
@@ -85,7 +85,7 @@ interface RuntimeDeps {
getMiddleware(): Promise<Middleware[]>;
assets: AssetServer; // serves /__wrnexus/* (islands, reactive, hmr)
hasStyles?: boolean; // inject the global stylesheet link
hasUi?: boolean; // inject the Wire UI stylesheet (/__wrnexus/ui.css)
hasUi?: boolean; // inject the WrNexus UI stylesheet (/__wrnexus/ui.css)
theme?: ResolvedTheme; // enables /__wrnexus/theme.css + <html data-theme>
i18n?: ResolvedI18n; // enables ctx.t, <html lang>, {t:key} markers
inlineStyles?: string; // inline small prod stylesheets into <head>
+16 -16
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.8.8",
"version": "0.8.16",
"type": "module",
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
"license": "MIT",
@@ -41,21 +41,21 @@
}
},
"dependencies": {
"@wrnexus/authz": "^0.8.8",
"@wrnexus/rpc": "^0.8.8",
"@wrnexus/core": "^0.8.8",
"@wrnexus/dev-toolbar": "^0.8.8",
"@wrnexus/router": "^0.8.8",
"@wrnexus/ssr": "^0.8.8",
"@wrnexus/csr": "^0.8.8",
"@wrnexus/compiler": "^0.8.8",
"@wrnexus/styles": "^0.8.8",
"@wrnexus/ui": "^0.8.8",
"@wrnexus/validation": "^0.8.8",
"@wrnexus/i18n": "^0.8.8",
"@wrnexus/db": "^0.8.8",
"@wrnexus/pubsub": "^0.8.8",
"@wrnexus/uploader": "^0.8.8",
"@wrnexus/authz": "^0.8.9",
"@wrnexus/rpc": "^0.8.9",
"@wrnexus/core": "^0.8.9",
"@wrnexus/dev-toolbar": "^0.8.9",
"@wrnexus/router": "^0.8.9",
"@wrnexus/ssr": "^0.8.9",
"@wrnexus/csr": "^0.8.16",
"@wrnexus/compiler": "^0.8.9",
"@wrnexus/styles": "^0.8.10",
"@wrnexus/ui": "^0.8.12",
"@wrnexus/validation": "^0.8.9",
"@wrnexus/i18n": "^0.8.9",
"@wrnexus/db": "^0.8.9",
"@wrnexus/pubsub": "^0.8.9",
"@wrnexus/uploader": "^0.8.9",
"@wrnexus/plugin": "^0.8.8",
"@wrnexus/store": "^0.8.8",
"@wrnexus/security": "^0.8.8",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-toolbar",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
"license": "MIT",
-67
View File
@@ -1,67 +0,0 @@
# @wrnexus/encryption
Authenticated encryption, hashing, HMAC, key rotation, and optional encrypted HTTP exchanges for WRNexusJS.
## Core helpers
- `generateKey()` — random 256-bit AES key encoded as base64.
- `deriveKey(password, salt)` — PBKDF2-derived AES key.
- `encrypt(plaintext, key)` / `decrypt(payload, key)` — AES-256-GCM.
- `sha256(data)` — SHA-256 digest.
- `hmacSign(data, secret)` / `hmacVerify(...)` — HMAC-SHA256.
- `createKeyring(keys)` — active/previous key management.
- `seal()` / `open()` — versioned ciphertext with key ID.
## Encrypted HTTP envelope
```ts
import {
createEncryptedRequest,
createKeyring,
createMemoryReplayStore,
decryptEncryptedResponse,
encryptedExchange,
} from "@wrnexus/encryption";
const keyring = createKeyring([{ id: "2026-08", secret: process.env.API_BODY_KEY!, active: true }]);
const replayStore = createMemoryReplayStore();
// Server middleware.
app.use(
encryptedExchange({
keyring,
replayStore,
maxAgeMs: 60_000,
maxBodyBytes: 1_048_576,
}),
);
// Controlled service/native client.
const request = await createEncryptedRequest(
"https://api.example.com/private/report",
{ reportId: "report-1" },
{ method: "POST", keyring },
);
const response = await fetch(request);
const result = await decryptEncryptedResponse(response, request, { keyring });
```
The envelope binds authenticated ciphertext to:
- HTTP method
- URL path and query
- request ID
- timestamp and expiry window
- encryption key ID
- optional replay-store consumption
`encryptedBody()` decrypts request bodies only. `encryptedExchange()` also encrypts successful downstream responses while allowing application exceptions to propagate normally. `encryptedFetch()` provides a convenient controlled-client call.
## Security boundary
Encrypted HTTP bodies **do not replace TLS/HTTPS**. Always use HTTPS.
This layer is appropriate for service-to-service traffic, native/mobile applications, controlled agents, and selected fields protected with server-managed keys. It cannot conceal data from an end user when browser JavaScript receives the decryption key. Never ship a long-lived server encryption key to a browser.
Use a shared replay store such as Redis in multi-instance deployments. The memory replay store is process-local.
-46
View File
@@ -1,46 +0,0 @@
{
"name": "@wrnexus/encryption",
"version": "0.8.8",
"type": "module",
"description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/encryption"
},
"homepage": "https://wrnexusjs.dev/packages/encryption",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"encryption"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
-81
View File
@@ -1,81 +0,0 @@
# @wrnexus/helpers
Safe convenience helpers for common WrNexus application flows. The package uses
standard `Context`, `URL`, and `Response` values and has no runtime dependency beyond
`@wrnexus/core`.
## Installation
```bash
bun add @wrnexus/helpers
```
The package is private, so the machine must be authenticated to the `wrnexus` npm
organization.
## Usage
### Redirect an unauthenticated forward-auth request
The gateway calls an SSO verifier on a different URL from the original application.
These helpers reconstruct the original URL from the gateway headers and safely place it
in the login redirect:
```ts
import type { Context } from "@wrnexus/core";
import { redirectToLogin } from "@wrnexus/helpers";
export const GET = async (ctx: Context) => {
if (await hasValidSession(ctx)) {
return new Response(null, { status: 204 });
}
return redirectToLogin(ctx, "/login", {
allowedHosts: ["admin.localhost:3000", "reports.localhost:3000"],
});
};
```
This creates a response such as:
```text
Location: http://sso.localhost:3000/login?returnTo=http%3A%2F%2Fadmin.localhost%3A3000%2F
```
Always list the application hosts that are valid redirect destinations. Forwarded host
headers are rejected when `allowedHosts` is absent or does not match, preventing an open
redirect.
The SSO hostname is the login destination, not an `allowedHosts` entry. For example,
when protecting `admin.localhost:3000`, keep `admin.localhost:3000` in the allowlist even
though the verifier runs at `sso.localhost:3000`. WRNexus preserves both hosts across a
nested gateway request.
### Support dynamic tenant domains
```ts
import type { Context } from "@wrnexus/core";
import { getOriginalRequestOrigin, redirectToLogin } from "@wrnexus/helpers";
export const GET = async (ctx: Context) => {
const allowedHosts = (host: string) => host === "example.test" || host.endsWith(".example.test");
console.info("Authentication requested by", getOriginalRequestOrigin(ctx, { allowedHosts }));
return redirectToLogin(ctx, "https://auth.example.test/login", {
allowedHosts,
returnToParam: "continue",
status: 303,
});
};
```
## API
- `getOriginalRequestUrl(ctx, options): URL` — reconstruct the gateway URL.
- `getOriginalRequestOrigin(ctx, options): string` — return only its origin.
- `getOriginalRequestPath(ctx): string` — return its path and query string.
- `getOriginalRequestMethod(ctx): string` — return its HTTP method.
- `redirectToLogin(ctx, loginUrl, options): Response` — create a login redirect with an
encoded `returnTo` parameter.
For direct requests without gateway headers, URL helpers use `ctx.url`.
-46
View File
@@ -1,46 +0,0 @@
{
"name": "@wrnexus/helpers",
"version": "0.8.8",
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/helpers"
},
"homepage": "https://wrnexusjs.dev/packages/helpers",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"helpers"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
+2 -2
View File
@@ -22,7 +22,7 @@ const i18n = resolveI18n(loadLocales("app/locales", { strict: true }), {
default: "en",
locales: ["en", "mr", "hi"],
fallbacks: { "mr-IN": ["mr", "en"] },
cookie: { name: "wire-lang", sameSite: "Lax", secure: true },
cookie: { name: "wrn-lang", sameSite: "Lax", secure: true },
});
const lang = resolveLang(i18n, cookieValue, request.headers.get("accept-language"));
@@ -57,7 +57,7 @@ Enable `i18nPlugin()` to use:
- `<LanguageSwitcher />`
- `<LocaleStatus />`
`LanguageSwitcher` renders a native `select[data-wire-lang]`. The packaged runtime validates the
`LanguageSwitcher` renders a native `select[data-wrn-lang]`. The packaged runtime validates the
selection against the configured locales, writes the configured language cookie, updates the
document `lang`/`dir` attributes, emits `wrnexus:language-change`, and reloads so the next SSR
request uses the same cookie. No application-owned browser script is required.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.8.8",
"version": "0.8.9",
"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.8",
"@wrnexus/core": "^0.8.9",
"@wrnexus/plugin": "^0.8.8",
"@wrnexus/ui": "^0.8.8"
"@wrnexus/ui": "^0.8.12"
},
"wrnexus": {
"plugin": {
-191
View File
@@ -1,191 +0,0 @@
# @wrnexus/jwt
> Dependency-free JSON Web Tokens (HS256) via Web Crypto, plus a bearer-token auth middleware for WrNexus.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
`@wrnexus/jwt` signs and verifies stateless JSON Web Tokens using the **HS256**
(HMAC-SHA-256) algorithm. It has no runtime dependencies — signing and
verification are implemented directly on the standard **Web Crypto** API
(`crypto.subtle`), which Bun provides natively. It runs server-side and pairs
with the session-based auth in `@wrnexus/core`, giving you a stateless option
for API and mobile clients. Reach for it when you need bearer-token auth rather
than cookie sessions.
## Installation
```bash
bun add @wrnexus/jwt
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
Single entry point (`@wrnexus/jwt`). All functions are async and return Promises.
| Export | Kind | Description |
| --------------------------------------- | --------- | --------------------------------------------------------------- |
| `signJwt(payload, secret, options?)` | function | Sign claims into an HS256 token string. |
| `verifyJwt<T>(token, secret, options?)` | function | Verify a token and return its claims, or throw. |
| `jwtAuth(options)` | function | Middleware that verifies a bearer JWT and sets `ctx.user`. |
| `JwtError` | class | Error thrown on any signature/payload/expiry failure. |
| `JwtClaims` | interface | Claims shape (`sub`, `iat`, `exp`, `nbf`, plus arbitrary keys). |
| `SignOptions` | interface | Options for `signJwt`. |
| `JwtAuthOptions` | interface | Options for `jwtAuth`. |
### `signJwt(payload, secret, options?)`
```ts
function signJwt(payload: JwtClaims, secret: string, options?: SignOptions): Promise<string>;
```
Signs `payload` with `secret` using HS256 and returns the encoded token
(`header.body.signature`). An `iat` (issued-at) claim is always added.
`SignOptions`:
- `expiresIn?: number` — seconds until expiry; sets the `exp` claim.
- `now?: number` — override the issued-at time (seconds), useful for testing.
### `verifyJwt<T>(token, secret, options?)`
```ts
function verifyJwt<T extends JwtClaims = JwtClaims>(
token: string,
secret: string,
options?: { now?: number },
): Promise<T>;
```
Verifies the HS256 signature and returns the decoded claims typed as `T`.
Throws `JwtError` when the token is malformed, the signature is invalid, the
payload is not valid JSON, the token is expired (`exp`), or not yet valid
(`nbf`). Pass `now` (seconds) to override the reference time for the `exp`/`nbf`
checks.
### `jwtAuth(options)`
```ts
function jwtAuth(options: JwtAuthOptions): Middleware;
```
Returns a WrNexus `Middleware` that reads a token, verifies it, and assigns the
claims to `ctx.user`.
`JwtAuthOptions`:
- `secret: string` — the HMAC secret used to verify tokens.
- `getToken?: (ctx: Context) => string | undefined` — how to extract the token.
Defaults to reading `Authorization: Bearer <token>`.
- `required?: boolean` — when `true` (default), a missing or invalid token
responds with `401 { ok: false, error: "Unauthorized" }`. When `false`,
requests pass through and `ctx.user` is only set if a valid token is present.
## Usage
```ts
import { signJwt, verifyJwt, jwtAuth, JwtError } from "@wrnexus/jwt";
const secret = process.env.JWT_SECRET!;
// Sign a token that expires in one hour
const token = await signJwt({ sub: user.id, role: "admin" }, secret, {
expiresIn: 3600,
});
// Verify it later
try {
const claims = await verifyJwt<{ sub: string; role: string }>(token, secret);
console.log(claims.sub, claims.role);
} catch (err) {
if (err instanceof JwtError) {
// invalid signature, expired, malformed, etc.
}
}
```
Protecting routes with the middleware:
```ts
import { jwtAuth } from "@wrnexus/jwt";
// Require a valid bearer token; ctx.user holds the verified claims
app.use(jwtAuth({ secret: process.env.JWT_SECRET! }));
// Optional auth — populate ctx.user when present, but don't 401
app.use(jwtAuth({ secret: process.env.JWT_SECRET!, required: false }));
```
## Requirements / Notes
- **Bun-only.** Uses the standard Web Crypto API (`crypto.subtle.importKey`,
`sign`, `verify`) plus `btoa`/`atob` and `TextEncoder`/`TextDecoder` — all
provided by Bun. No third-party crypto dependency.
- **Algorithm:** HS256 (HMAC with SHA-256) only. Asymmetric algorithms (RS/ES)
are not supported.
- Integrates with [`@wrnexus/core`](../core) for `Context`, `Middleware`, and
`ctx.user`; it complements the framework's cookie/session auth with a
stateless bearer-token flow for API and mobile clients.
## Access, refresh, scope, and cookie helpers
```ts
import {
createAccessToken,
createRefreshToken,
verifyAccessToken,
verifyRefreshToken,
extractBearerToken,
requireScopes,
jwtCookie,
} from "@wrnexus/jwt";
```
The helpers add explicit `type: "access" | "refresh"` claims, scope checks, refresh-token family metadata, no-store token responses, and secure cookie defaults. `__Host-` cookies are rejected unless they use `Path=/` and `Secure`; `SameSite=None` is rejected without `Secure`.
## 0.8 helper kit
```ts
import {
createTokenPair,
verifyAccessToken,
verifyRefreshToken,
extractBearerToken,
readJwtCookie,
jwtCookie,
clearJwtCookie,
requireScopes,
} from "@wrnexus/jwt";
const pair = await createTokenPair(user.id, {
accessSecret: process.env.JWT_ACCESS_SECRET!,
refreshSecret: process.env.JWT_REFRESH_SECRET!,
scopes: ["profile:read"],
family: sessionFamily,
});
```
The helper kit validates `__Host-` cookie invariants, cookie names and paths, `SameSite=None` security, typed access/refresh token types, scope requirements, and no-store token responses.
In addition to local HS256 secrets/keyrings, the package verifies standards-based
RS256 tokens through bounded remote JWKS caches:
```ts
import { createRemoteJwks, verifyJwtWithJwks } from "@wrnexus/jwt";
const jwks = createRemoteJwks("https://issuer.example/.well-known/jwks.json");
const claims = await verifyJwtWithJwks(token, jwks, {
issuer: "https://issuer.example",
audience: "my-api",
maxAge: 300,
});
```
JWKS URLs must use HTTPS. Responses have key-count/byte limits, accept only
RS256 signing RSA keys, deduplicate concurrent refreshes, cache imported public
keys, and force an immediate refresh for an unknown `kid` so issuer rotation
does not wait for cache expiry. Never use decoded-but-unverified claims for an
authorization decision.
-46
View File
@@ -1,46 +0,0 @@
{
"name": "@wrnexus/jwt",
"version": "0.8.8",
"type": "module",
"description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/jwt"
},
"homepage": "https://wrnexusjs.dev/packages/jwt",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"jwt"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
-91
View File
@@ -1,91 +0,0 @@
# @wrnexus/mobile
> SSR-safe access to Capacitor plugins from WRNexusJS browser code.
## Overview
`@wrnexus/mobile` keeps optional native imports out of server rendering while giving
browser-owned modules one consistent registry for Capacitor plugins. During SSR,
`mobile.isNative()` is `false` and `mobile.platform()` is `"web"`.
## Installation
Install a plugin through the WRNexusJS CLI so the web and native projects stay aligned:
```bash
wrnexus mobile add @capacitor/camera @capacitor/haptics
```
## Usage
### Register and invoke a Capacitor plugin
Import Capacitor packages only from browser-owned code, never from API routes or SSR
helpers.
```ts
import { Camera, CameraResultType } from "@capacitor/camera";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Camera", Camera);
export async function takePhoto() {
if (!mobile.isNative()) return null;
return mobile.invoke("Camera", "getPhoto", {
quality: 85,
resultType: CameraResultType.Uri,
});
}
```
### Provide a browser fallback
`whenNative` runs the first callback only in a Capacitor WebView and can return a
web/SSR-safe fallback everywhere else.
```ts
import { Haptics, ImpactStyle } from "@capacitor/haptics";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Haptics", Haptics);
export const confirmAction = () =>
mobile.whenNative(
() => mobile.invoke("Haptics", "impact", { style: ImpactStyle.Medium }),
() => navigator.vibrate?.(30),
);
```
### Read an optional plugin without throwing
```ts
import type { NetworkPlugin } from "@capacitor/network";
import { mobile } from "@wrnexus/mobile";
const network = mobile.plugin<NetworkPlugin>("Network");
const status = network ? await network.getStatus() : { connected: true, connectionType: "unknown" };
```
## API
- `registerPlugin(name, instance)` registers a browser-imported plugin.
- `plugin(name)` returns a plugin or `undefined`; `requirePlugin(name)` throws when absent.
- `invoke(plugin, method, options?)` calls a registered method and returns its result.
- `whenNative(native, fallback?)` selects native behavior without breaking SSR.
- `isNative()` and `platform()` report the current Capacitor environment.
Unavailable required plugins throw `MobileUnavailableError` with an actionable message.
The package also provides portable application-facing primitives:
- `listenDeepLinks` normalizes initial and live links with an allowed-scheme list.
- `PushNotifications` performs permission gating and validates registrations.
- `SecureStorage` namespaces and validates keys over an application-supplied encrypted
Keychain/Keystore adapter; it does not mislabel browser `localStorage` as secure.
- `OfflineQueue` persists bounded sync batches through a pluggable durable store.
## Requirements / Notes
- Capacitor plugin imports must remain in browser-owned modules.
- `@wrnexus/mobile` re-exports `native` from `@wrnexus/native` for applications that
prefer the higher-level cross-platform capability API.
-46
View File
@@ -1,46 +0,0 @@
{
"name": "@wrnexus/mobile",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/mobile — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/mobile"
},
"homepage": "https://wrnexusjs.dev/packages/mobile",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"mobile"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/native": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
-88
View File
@@ -1,88 +0,0 @@
# @wrnexus/native
> Cross-platform capabilities for browsers, Capacitor WebViews, and compiled native apps.
## Overview
`@wrnexus/native` exposes capabilities by name so application code can ask what the
current platform supports before presenting an action. Browser capabilities use Web
APIs; mobile capabilities use installed Capacitor plugins. `platform()` returns
`"server"` during SSR, `"browser"` on the web, and the Capacitor platform in a native
WebView.
## Installation
```bash
bun add @wrnexus/native
```
## Usage
### Share a page when the platform supports it
```ts
import { native } from "@wrnexus/native";
export async function shareCurrentPage() {
if (!native.supports("share")) return false;
await native.run("share", {
title: document.title,
url: location.href,
});
return true;
}
```
### Register an application-specific capability
`register` returns an unregister function, which is useful for tests and temporary
feature modules.
```ts
import { native } from "@wrnexus/native";
const unregister = native.register("orders.scan", {
browser: {
supported: () => typeof window !== "undefined",
run: async ({ orderId }: { orderId: string }) => {
const code = window.prompt(`Scan code for order ${orderId}`);
return { code };
},
},
});
const result = await native.run<{ code: string | null }>("orders.scan", { orderId: "ord_42" });
unregister();
```
### Target browser or mobile behavior explicitly
```ts
import { native } from "@wrnexus/native";
const canUseMobileCamera = native.supports("camera", "mobile");
const position = await native.run(
"geolocation",
{ enableHighAccuracy: true },
{ target: "browser" },
);
```
## API
- `supports(name, target?)` checks availability without running the capability.
- `run(name, options?, runOptions?)` executes it or rejects with `NativeUnavailableError`.
- `register(name, capability)` adds or overrides a capability and returns cleanup.
- `registered()` lists capability names; `clearRegistry()` resets the registry.
- `isMobile()` and `platform()` report the current target safely during SSR.
Built-ins include `camera`, `clipboard.write`, `share`, `geolocation`, `network`,
`haptics`, storage, filesystem, notifications, and device information.
`defineNativeManifest` declares required capabilities and typed permissions, while
`PermissionManager` normalizes permission query/request flows across platform adapters.
## Requirements / Notes
Use `supports()` before showing optional controls. Mobile capabilities require their
matching Capacitor plugins to be installed and registered by the application.
-51
View File
@@ -1,51 +0,0 @@
{
"name": "@wrnexus/native",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/native — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/native"
},
"homepage": "https://wrnexusjs.dev/packages/native",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"native"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./browser": {
"types": "./dist/browser.d.ts",
"import": "./dist/browser.js"
},
"./mobile": {
"types": "./dist/mobile.d.ts",
"import": "./dist/mobile.js"
}
},
"files": [
"dist",
"README.md"
]
}
-217
View File
@@ -1,217 +0,0 @@
# @wrnexus/oauth
> Dependency-free OAuth 2.0 sign-in for any provider, with PKCE and presets for Google, GitHub, and Discord.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
`@wrnexus/oauth` implements the OAuth 2.0 Authorization Code flow (with PKCE) for
server-side sign-in. It ships ready-made provider presets and a `defineProvider`
helper for custom providers, then gives you two flow functions — `startAuth`
(build the redirect) and `completeAuth` (exchange the code and fetch the user's
profile). It has no runtime dependencies: it uses the platform `fetch` and
WebCrypto only. Pairs naturally with `@wrnexus/core`'s `logIn` to establish a
session once you have a normalized profile.
## Installation
```bash
bun add @wrnexus/oauth
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
### Providers
Each preset takes `ProviderCredentials` and returns an `OAuthProvider`.
```ts
interface ProviderCredentials {
clientId: string;
clientSecret: string;
scopes?: string[]; // override the preset's default scopes
}
```
| Export | Default scopes | Notes |
| ------------------------ | ---------------------------- | ------------------------------------------------------------------ |
| `google(creds)` | `openid`, `email`, `profile` | Sets `access_type: offline` for refresh tokens. |
| `github(creds)` | `read:user`, `user:email` | Maps `name` (falls back to `login`) and `avatar_url`. |
| `discord(creds)` | `identify`, `email` | Builds the avatar CDN URL from the user id + hash. |
| `defineProvider(config)` | — | Pass a full `OAuthProvider` to define a custom OAuth 2.0 provider. |
An `OAuthProvider` describes the endpoints, scopes, credentials, optional extra
authorize params, and a `mapProfile` normalizer:
```ts
interface OAuthProvider {
name: string;
authorizeUrl: string;
tokenUrl: string;
userInfoUrl: string;
scopes: string[];
clientId: string;
clientSecret: string;
authorizeParams?: Record<string, string>; // e.g. access_type, prompt
mapProfile: (raw: Record<string, unknown>) => OAuthProfile;
}
```
### Flow
#### `startAuth(provider, options): Promise<StartAuthResult>`
Builds the authorize redirect URL with a generated PKCE challenge and CSRF
`state`. Store the returned `state` and `verifier` (session/cookie), then 302 the
user to `url`.
```ts
interface StartAuthOptions {
redirectUri: string;
state?: string; // reuse a state instead of generating one
params?: Record<string, string>; // extra authorize params, merged last
}
interface StartAuthResult {
url: string; // authorize URL to redirect to
state: string; // CSRF state — verify on callback
verifier: string; // PKCE code verifier — pass to completeAuth
}
```
#### `completeAuth(provider, options): Promise<{ tokens, profile }>`
On the callback: exchanges the authorization `code` for tokens, then fetches and
normalizes the user profile. Convenience wrapper over `exchangeCode` +
`fetchProfile`.
```ts
interface CompleteAuthOptions {
code: string;
redirectUri: string;
verifier?: string; // the PKCE verifier from startAuth
fetch?: typeof fetch; // inject a fetch implementation (tests)
}
```
#### Lower-level helpers
| Export | Signature | Purpose |
| ---------------------------------------- | ------------------------- | --------------------------------------------------------------- |
| `exchangeCode(provider, options)` | `→ Promise<OAuthTokens>` | Exchange an authorization code for tokens. |
| `fetchProfile(provider, tokens, fetch?)` | `→ Promise<OAuthProfile>` | Fetch + normalize the user's profile. |
| `randomToken(bytes?)` | `→ string` | Random URL-safe token (default 32 bytes) for `state`/verifiers. |
### Types
```ts
interface OAuthTokens {
access_token: string;
token_type?: string;
refresh_token?: string;
expires_in?: number;
id_token?: string;
scope?: string;
}
interface OAuthProfile {
id: string;
email?: string;
name?: string;
avatar?: string;
raw: Record<string, unknown>;
}
```
## Usage
```ts
import { google, startAuth, completeAuth } from "@wrnexus/oauth";
import { logIn } from "@wrnexus/core";
const provider = google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
});
const redirectUri = "https://example.com/auth/callback";
// 1. Kick off sign-in: redirect the user to the provider.
async function beginLogin(ctx) {
const { url, state, verifier } = await startAuth(provider, { redirectUri });
// Persist state + verifier in the session, then redirect.
ctx.session.set("oauth_state", state);
ctx.session.set("oauth_verifier", verifier);
return Response.redirect(url, 302);
}
// 2. Handle the callback.
async function handleCallback(ctx, code: string, state: string) {
if (state !== ctx.session.get("oauth_state")) throw new Error("bad state");
const { profile } = await completeAuth(provider, {
code,
redirectUri,
verifier: ctx.session.get("oauth_verifier"),
});
logIn(ctx, { id: profile.id, email: profile.email });
}
```
Custom provider with `defineProvider`:
```ts
import { defineProvider, startAuth } from "@wrnexus/oauth";
const gitlab = defineProvider({
name: "gitlab",
authorizeUrl: "https://gitlab.com/oauth/authorize",
tokenUrl: "https://gitlab.com/oauth/token",
userInfoUrl: "https://gitlab.com/api/v4/user",
scopes: ["read_user"],
clientId: process.env.GITLAB_CLIENT_ID!,
clientSecret: process.env.GITLAB_CLIENT_SECRET!,
mapProfile: (raw) => ({
id: String(raw.id),
email: raw.email as string | undefined,
name: raw.name as string | undefined,
avatar: raw.avatar_url as string | undefined,
raw,
}),
});
```
## Requirements / Notes
- **Bun-only.** Relies on the global `fetch` and WebCrypto (`crypto.getRandomValues`,
`crypto.subtle.digest`) — no other runtime dependencies.
- The flow is stateless by design: you are responsible for storing `state` and
`verifier` between `startAuth` and `completeAuth` (session or signed cookie).
- Pairs with [`@wrnexus/core`](../core) — feed the normalized `OAuthProfile` into
`logIn` to establish a session.
OIDC integrations can combine strict discovery with the rotating JWKS verifier:
```ts
import { createRemoteJwks } from "@wrnexus/jwt";
import { discoverOidc, verifyOidcIdToken } from "@wrnexus/oauth";
const metadata = await discoverOidc("https://issuer.example");
const jwks = createRemoteJwks(metadata.jwks_uri);
const claims = await verifyOidcIdToken(idToken, {
issuer: metadata.issuer,
clientId: "client-id",
jwks,
nonce: expectedNonce,
accessToken,
});
```
Discovery requires an exact normalized issuer and HTTPS endpoints without URL
credentials/fragments. ID-token verification checks the RS256 signature,
expiry/not-before, issuer, audience, required OIDC claims, nonce, multi-audience
`azp`, optional token age, and optional `at_hash` binding.
-46
View File
@@ -1,46 +0,0 @@
{
"name": "@wrnexus/oauth",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/oauth — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/oauth"
},
"homepage": "https://wrnexusjs.dev/packages/oauth",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"oauth"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/jwt": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
-60
View File
@@ -1,60 +0,0 @@
# @wrnexus/plugin
## Least-privilege package permissions
Package manifests declare every framework capability they register:
```json
{
"wrnexus": {
"permissions": ["routes", "migrations"],
"routes": [{ "kind": "api", "path": "/api/example", "entry": "./route.ts" }]
}
}
```
Applications can enable fail-closed grants:
```ts
export default {
pluginPermissions: {
enforce: true,
grants: { "example-plugin": ["routes"] },
},
};
```
Discovery rejects used-but-undeclared capabilities with
`WRN-PLUGIN-PERMISSION-UNDECLARED` and ungranted capabilities with
`WRN-PLUGIN-PERMISSION-DENIED`. Permissions cover components, browser runtime,
assets, styles, routes, middleware, migrations, config, transforms,
diagnostics/tooling, and server/build hooks.
## Compatibility matrices
Manifests can add `compatibility: { bunMin: "1.3.0", os: ["linux",
"darwin"] }` alongside `runtimes` and `requires`. Use
`testPluginCompatibility(manifest, targets)` in a package test to exercise the
complete support matrix. Runtime discovery enforces the same Bun minimum, OS,
runtime, and capability declarations used by the test kit.
Deterministic WRNexusJS plugin contracts for configuration, AST/code transforms,
diagnostics, development servers, production builds, and DevToolbar extensions.
Use `definePlugin()` and declare `enforce`, `before`, or `after` when ordering matters.
Duplicate names and dependency cycles are rejected.
## Complete lifecycle and contributions
Plugins may implement `setup`, `configure`, `configResolved`, `transformAst`,
`transformCode`, `diagnostics`, `routes`, `configureServer`, `buildStart`,
`buildEnd`, `render`, `deploy`, `shutdown`, and `hmrUpdate`. The runner preserves
resolved plugin order for every hook and executes `setup` exactly once.
In addition to components, routes, middleware, assets, styles, runtimes, and
migrations, plugins can contribute `directives`, `cliCommands`,
`virtualModules`, `deploymentAdapters`, `configSchemas`, `documentation`, and
`typeDefinitions`. Names are collision checked. Configuration schemas run after
configuration resolution, CLI commands are callable as normal `wrnexus`
commands, directives participate in AST transformation, and production builds
materialize virtual modules and invoke matching contributed adapters.
-58
View File
@@ -1,58 +0,0 @@
{
"name": "@wrnexus/plugin",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/plugin — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/plugin"
},
"homepage": "https://wrnexusjs.dev/packages/plugin",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"plugin"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./types": {
"types": "./dist/types.d.ts",
"import": "./dist/types.js"
},
"./manifest": {
"types": "./dist/manifest.d.ts",
"import": "./dist/manifest.js"
},
"./discovery": {
"types": "./dist/discovery.d.ts",
"import": "./dist/discovery.js"
}
},
"dependencies": {
"@wrnexus/syntax": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
"license": "MIT",
@@ -45,8 +45,8 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8",
"@wrnexus/rpc": "^0.8.8"
"@wrnexus/core": "^0.8.9",
"@wrnexus/rpc": "^0.8.9"
},
"files": [
"dist",
-211
View File
@@ -1,211 +0,0 @@
# @wrnexus/queue
> A background job queue with delays, retries + exponential backoff, recurring jobs, and concurrent workers.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
`@wrnexus/queue` is a server-side in-process job queue. You register named
workers, enqueue jobs (optionally delayed or recurring), and let the queue poll
and run them on a timer — with per-job retry limits and doubling backoff between
attempts. The default store lives in memory; the design allows a pluggable driver
to back it with Redis/SQL for durability across restarts. Reach for it when you
need to defer work (emails, webhooks, cleanup) off the request path without a
heavyweight external broker. Tests can drive it deterministically via `drain()`.
## Installation
```bash
bun add @wrnexus/queue
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
The package exports a single factory plus its supporting types.
### `createQueue(options?): Queue`
Creates a new queue instance.
```ts
function createQueue(options?: QueueOptions): Queue;
```
#### `QueueOptions`
| Option | Type | Default | Description |
| ------------- | ------------------------------------ | ---------- | -------------------------------------------------------- |
| `maxAttempts` | `number` | `3` | Default max attempts per job before it is dead-lettered. |
| `backoffMs` | `number` | `1000` | Base retry backoff in ms; doubles per attempt. |
| `pollMs` | `number` | `250` | Poll interval used once `start()` is called (ms). |
| `onFailed` | `(job: Job, error: unknown) => void` | — | Called when a job exhausts its attempts. |
| `concurrency` | `number` | unlimited | Maximum jobs claimed by one `drain()` call. |
| `capacity` | `number` | unlimited | Maximum queued plus active jobs before adds reject. |
| `now` | `() => number` | `Date.now` | Clock injection for deterministic tests. |
### `Queue`
The object returned by `createQueue`.
| Method | Signature | Description |
| ---------- | -------------------------------------------------------------- | -------------------------------------------------------------- |
| `add` | `add<T>(name, data: T, options?: AddOptions): Promise<Job<T>>` | Enqueue a job under a worker name. Returns the created job. |
| `process` | `process<T>(name, handler: JobHandler<T>): void` | Register the worker that runs jobs of the given name. |
| `drain` | `drain(now?: number): Promise<number>` | Run every job whose `runAt ≤ now`, once. Returns how many ran. |
| `start` | `start(): void` | Begin polling every `pollMs`. No-op if already started. |
| `stop` | `stop(): void` | Stop the poll timer. |
| `shutdown` | `shutdown({ force? }): Promise<void>` | Stop accepting jobs and await active work; force aborts it. |
| `size` | `size(): number` | Number of jobs currently queued. |
| `get/list` | `get(id)` / `list(name?)` | Inspect defensive copies of pending jobs. |
| `cancel` | `cancel(id): boolean` | Remove queued work or abort an active handler. |
| `failed` | `failed(): Job[]` | Inspect exhausted jobs in the dead-letter collection. |
| `retry` | `retry(id): Promise<boolean>` | Reset and requeue a dead-lettered job. |
#### `AddOptions`
| Option | Type | Description |
| ---------------- | -------- | -------------------------------------------------------------------------- |
| `delayMs` | `number` | Delay before the job becomes runnable (ms). |
| `maxAttempts` | `number` | Max attempts before dead-lettering. Defaults to the queue's `maxAttempts`. |
| `repeat` | `number` | Re-enqueue this job this many ms after each successful run (recurring). |
| `priority` | `number` | Higher values are selected first among due jobs. |
| `idempotencyKey` | `string` | Return the matching pending job instead of enqueueing a duplicate. |
#### `JobHandler<T>`
```ts
type JobHandler<T = unknown> = (
job: Job<T>,
context: { signal: AbortSignal },
) => void | Promise<void>;
```
#### `Job<T>`
```ts
interface Job<T = unknown> {
id: string; // e.g. "job_1"
name: string;
data: T;
attempts: number;
maxAttempts: number;
runAt: number; // epoch ms; job runs when now ≥ runAt
repeat?: number; // if set, re-enqueue this many ms after each success
}
```
## Usage
Register workers, enqueue jobs, then start the poller:
```ts
import { createQueue } from "@wrnexus/queue";
const queue = createQueue({ maxAttempts: 3, backoffMs: 1000 });
// Register a worker for the "email" job name.
queue.process<{ to: string }>("email", async (job) => {
await send(job.data.to);
});
// Enqueue a delayed job with up to 3 attempts.
await queue.add("email", { to: "a@b.com" }, { delayMs: 5000, maxAttempts: 3 });
queue.start(); // begin polling; queue.stop() to halt
```
Use `context.signal` in network/database calls so forced shutdown and active
cancellation finish promptly. For process termination, prefer
`await queue.shutdown()`; use `{ force: true }` only after your grace period.
### Durable queue
`createDurableQueue({ store })` retains jobs until their handler succeeds and
supports atomic leases when a driver implements `QueueStore.claim`. It exposes
the same cancellation/shutdown behavior plus `list`, `failed`, and `retry`.
The included `memoryQueueStore()` is useful for tests; production Redis/SQL
drivers should make `claim()` atomic to prevent two workers executing one job.
### Recurring jobs
Pass `repeat` to re-enqueue a job a fixed interval after each successful run:
```ts
queue.process("heartbeat", async () => ping());
await queue.add("heartbeat", {}, { repeat: 60_000 }); // runs ~every minute
```
### Handling permanent failures
When a job's `attempts` reaches `maxAttempts`, it is dropped and `onFailed`
fires instead of retrying:
```ts
const queue = createQueue({
onFailed: (job, error) => {
console.error(`job ${job.id} (${job.name}) gave up`, error);
},
});
```
### Deterministic testing
Instead of `start()`, inject a clock and drive the queue with `drain()`:
```ts
let clock = 0;
const queue = createQueue({ now: () => clock });
queue.process("task", async () => {
/* ... */
});
await queue.add("task", {}, { delayMs: 5000 });
clock = 5000;
const ran = await queue.drain(); // => 1
```
### Durable workflows and approvals
`createWorkflowEngine(store)` executes dependency-ordered steps and persists every transition,
result, progress update, failure, cancellation, and approval record. Approval steps pause safely
and can resume after a process restart because the snapshot lives in the supplied `WorkflowStore`.
```ts
const workflow = defineDurableWorkflow({
name: "publish-report",
steps: [
{ name: "build", run: buildReport },
{ name: "approve", dependsOn: ["build"], approval: true, run: (report) => report },
{ name: "publish", dependsOn: ["approve"], run: publishReport },
],
});
const run = await engine.start(workflow, input);
await engine.approve(workflow, run.id, "approve", currentUser.id);
```
Use `memoryWorkflowStore()` for tests. Production stores implement the small `get`, `put`, and
`list` contract using the same transactional database or durable service as the application.
## Retry & backoff behavior
- On a thrown handler error, the job is retried while `attempts < maxAttempts`.
- The next `runAt` is set to `now + backoffMs * 2^(attempts - 1)` (exponential
backoff): with `backoffMs: 1000` the delays are 1s, 2s, 4s, …
- A job whose worker name has no registered handler stays queued until one is
registered (it is not counted as runnable by `drain`).
- `drain` is re-entrant-safe: overlapping calls are skipped while one is running.
## Requirements / Notes
- **Bun-only** runtime (Node is not supported), consistent with the rest of the
WrNexus framework. The queue itself relies only on standard timers
(`setInterval`/`clearInterval`) and has no runtime dependencies.
- The default store is in-process, so queued jobs do not survive a restart; a
pluggable driver is intended for backing it with Redis/SQL for durability.
- Works alongside `@wrnexus/core` for offloading work from the request path.
-47
View File
@@ -1,47 +0,0 @@
{
"name": "@wrnexus/queue",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/queue — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/queue"
},
"homepage": "https://wrnexusjs.dev/packages/queue",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"queue"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8",
"@wrnexus/rpc": "^0.8.8"
},
"files": [
"dist",
"README.md"
]
}
-99
View File
@@ -1,99 +0,0 @@
# @wrnexus/reactive
> Tiny, type-safe reactive primitives (signals) with zero dependencies.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
`@wrnexus/reactive` is the seed of WrNexus's reactivity layer: a minimal `signal`
primitive that holds a value, notifies subscribers when it changes, and hands back
an unsubscribe function. It is deliberately small and framework-agnostic — it powers
nothing on its own, but is shaped so client islands (and later the `.wrn` compiler's
`state` blocks) can build reactive bindings on top of it. Reach for it when you need
observable state without pulling in a full reactivity library.
## Installation
```bash
bun add @wrnexus/reactive
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
The package has a single entry point (`.`) exporting one function and three types.
### `signal<T>(initial: T): Signal<T>`
Creates a reactive signal seeded with `initial`. Returns a `Signal<T>`:
| Member | Signature | Description |
| ----------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `get` | `(): T` | Read the current value. |
| `set` | `(next: T): void` | Write a new value. Subscribers run **only when the value actually changes** (compared with `Object.is`). |
| `update` | `(fn: (current: T) => T): void` | Apply a function to the current value; equivalent to `set(fn(get()))`. |
| `subscribe` | `(fn: Subscriber<T>): Unsubscribe` | Register a subscriber; returns a function that removes it. |
### Types
```ts
type Subscriber<T> = (value: T) => void;
type Unsubscribe = () => void;
interface Signal<T> {
get(): T;
set(next: T): void;
update(fn: (current: T) => T): void;
subscribe(fn: Subscriber<T>): Unsubscribe;
}
```
Notes on semantics:
- **No-op updates are skipped.** `set` compares the incoming value to the current
one with `Object.is`; identical values do not notify subscribers.
- **Safe unsubscribe during notification.** Subscribers are iterated over a copy of
the subscriber set, so a subscriber may call its own (or another's) unsubscribe
while a notification is in flight.
## Usage
```ts
import { signal } from "@wrnexus/reactive";
const count = signal(0);
count.get(); // 0
// Subscribe; the returned function unsubscribes.
const off = count.subscribe((value) => {
console.log("count is now", value);
});
count.set(1); // logs: count is now 1
count.set(1); // no-op — value unchanged, no notification
count.update((n) => n + 1); // logs: count is now 2
off(); // stop listening
count.set(3); // nothing logged
```
Typed signals infer `T` from the initial value, or can be annotated explicitly:
```ts
import { signal, type Signal } from "@wrnexus/reactive";
const user: Signal<{ name: string } | null> = signal(null);
user.set({ name: "Ada" });
```
## Requirements / Notes
- **Bun-only.** Distributed as TypeScript source (`main`/`exports` point at
`src/index.ts`); consume it under Bun, which runs `.ts` directly.
- **Zero dependencies.** The only runtime API used is the standard `Object.is`.
- Foundational primitive for WrNexus client islands and the forthcoming `.wrn`
compiler `state` blocks.
-43
View File
@@ -1,43 +0,0 @@
{
"name": "@wrnexus/reactive",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/reactive — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/reactive"
},
"homepage": "https://wrnexusjs.dev/packages/reactive",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"reactive"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist",
"README.md"
]
}
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/router — part of the WrNexus framework.",
"license": "MIT",
@@ -37,8 +37,8 @@
}
},
"dependencies": {
"@wrnexus/compiler": "^0.8.8",
"@wrnexus/core": "^0.8.8"
"@wrnexus/compiler": "^0.8.9",
"@wrnexus/core": "^0.8.9"
},
"files": [
"dist",
+1 -1
View File
@@ -89,7 +89,7 @@ const html = renderDocument({
},
url: new URL("https://acme.example/about"),
body: "<h1>About Us</h1>",
scripts: ["/_wire/runtime.js", "/_wire/islands/about.js"],
scripts: ["/_wrn/runtime.js", "/_wrn/islands/about.js"],
htmlAttrs: ' data-theme="dark"',
});
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/ssr — part of the WrNexus framework.",
"license": "MIT",
@@ -45,7 +45,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.8.8",
"@wrnexus/core": "^0.8.9",
"@wrnexus/store": "^0.8.8",
"@wrnexus/security": "^0.8.8"
},
+13 -13
View File
@@ -21,7 +21,7 @@ other arrays intentionally replace earlier values. Cycles and missing/invalid
entries fail with stable `WRN-CONFIG-LAYER-*` diagnostics. `wrnexus config
--explain` lists every resolved layer source.
> Global CSS bundling, the `--wire-*` design-token theme system, and the `wrnexus.config.ts` app-config loader for WrNexus apps.
> Global CSS bundling, the `--wrn-*` design-token theme system, and the `wrnexus.config.ts` app-config loader for WrNexus apps.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
@@ -30,7 +30,7 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr
This package owns three server-side concerns that shape every page a WrNexus app renders:
1. **Global stylesheet pipeline** — finds `app/styles/global.css` (or aggregates `app/styles/*.css`), bundles it with Bun's CSS bundler (which resolves `@import`, including from `node_modules`), and produces one stylesheet that is `<link>`ed into every page's `<head>`. Because it is a plain global sheet, it styles server-rendered markup and hydrated client islands identically. A custom `process` hook lets you swap in Tailwind / PostCSS / Sass.
2. **Theme system** — design tokens exposed as CSS custom properties (`--wire-<key>`), with built-in `light`/`dark` sets, deep-merged user overrides, an SSR `<html data-theme>` render (no flash), and a tiny client runtime to toggle/persist the choice.
2. **Theme system** — design tokens exposed as CSS custom properties (`--wrn-<key>`), with built-in `light`/`dark` sets, deep-merged user overrides, an SSR `<html data-theme>` render (no flash), and a tiny client runtime to toggle/persist the choice.
3. **App config** — loads `wrnexus.config.ts` (the `AppConfig` type), applies named profile overrides, and loads the `.env` cascade.
It runs server-side / at build time. Reach for it when configuring an app, defining themes, or customising how global CSS is produced.
@@ -111,7 +111,7 @@ interface StyleProcessContext {
| Export | Type / Signature | Purpose |
| -------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `DEFAULT_THEMES` | `Record<string, ThemeTokens>` | Built-in `light` and `dark` token maps. |
| `THEME_COOKIE` | `"wire-theme"` | Cookie the resolved theme is read from / persisted to. |
| `THEME_COOKIE` | `"wrn-theme"` | Cookie the resolved theme is read from / persisted to. |
| `THEME_CSS_HREF` | `"/__wrnexus/theme.css"` | URL the generated theme stylesheet is served at. |
| `THEME_JS_HREF` | `"/__wrnexus/theme.js"` | URL the client theme runtime is served at. |
| `resolveThemeConfig` | `(config?: ThemeConfig) => ResolvedTheme` | Deep-merge the user's `theme` config over the defaults; pick the default theme (config's `default` if valid, else `dark`, else the first). |
@@ -119,7 +119,7 @@ interface StyleProcessContext {
| `renderThemeCss` | `(theme: ResolvedTheme) => string` | Generate the theme stylesheet: a `:root{…}` default plus one `[data-theme="<name>"]{…}` block per theme. |
| `renderThemeRuntime` | `(theme: ResolvedTheme) => string` | Generate the client runtime (see below). |
Tokens are emitted as `--wire-<key>` custom properties, **except** the reserved key `color-scheme`, which is emitted as the native `color-scheme` CSS property so form controls and scrollbars match the theme.
Tokens are emitted as `--wrn-<key>` custom properties, **except** the reserved key `color-scheme`, which is emitted as the native `color-scheme` CSS property so form controls and scrollbars match the theme.
`ThemeConfig` / `ThemeTokens` / `ResolvedTheme`:
@@ -164,7 +164,7 @@ theme: {
}
```
The client runtime (`renderThemeRuntime`) exposes `window.wireTheme` with `{ get, set, toggle, bind, themes }`, wires up any `[data-wire-theme-toggle]` and `[data-wire-theme-set]` elements on load, and persists the choice to the `wire-theme` cookie (`max-age` 1 year, `samesite=lax`). `toggle()` cycles through the configured theme names in order.
The client runtime (`renderThemeRuntime`) exposes `window.wrnTheme` with `{ get, set, toggle, bind, themes }`, connects up any `[data-wrn-theme-toggle]` and `[data-wrn-theme-set]` elements on load, and persists the choice to the `wrn-theme` cookie (`max-age` 1 year, `samesite=lax`). `toggle()` cycles through the configured theme names in order.
## Usage
@@ -252,17 +252,17 @@ In templates, consume tokens via the custom properties:
```css
.card {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
box-shadow: var(--wire-shadow-1);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
box-shadow: var(--wrn-shadow-1);
}
```
```html
<button data-wire-theme-toggle>Toggle theme</button>
<button data-wire-theme-set="brand">Brand theme</button>
<button data-wrn-theme-toggle>Toggle theme</button>
<button data-wrn-theme-set="brand">Brand theme</button>
```
## Requirements / Notes
@@ -270,4 +270,4 @@ In templates, consume tokens via the custom properties:
- **Bun-only.** `bundleCss` uses `Bun.build`'s CSS bundler for `@import` resolution, nesting, and minification. Node is not supported.
- Config and env loading use `node:fs` / `node:path` / `node:url` and read from `process.env`.
- Peer package: `@wrnexus/core` supplies the `SeoConfig` and `SecurityConfig` types referenced by `AppConfig`.
- The bundled global stylesheet, the theme stylesheet (`THEME_CSS_HREF`), and the theme runtime (`THEME_JS_HREF`) are wired into pages by the framework's server; this package only produces their contents.
- The bundled global stylesheet, the theme stylesheet (`THEME_CSS_HREF`), and the theme runtime (`THEME_JS_HREF`) are connected into pages by the framework's server; this package only produces their contents.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.8.8",
"version": "0.8.10",
"type": "module",
"description": "@wrnexus/styles — part of the WrNexus framework.",
"license": "MIT",
@@ -37,8 +37,8 @@
}
},
"dependencies": {
"@wrnexus/uploader": "^0.8.8",
"@wrnexus/core": "^0.8.8",
"@wrnexus/uploader": "^0.8.9",
"@wrnexus/core": "^0.8.9",
"@wrnexus/plugin": "^0.8.8"
},
"files": [
-7
View File
@@ -1,7 +0,0 @@
# @wrnexus/syntax
Canonical WRN lexer, parser, AST, language metadata, source positions, and stable
diagnostics. Framework tooling should import this package instead of implementing a
separate `.wrn` parser.
See `docs/WRN-LANGUAGE-SPEC-1.0.md` in the WRNexusJS repository.
-67
View File
@@ -1,67 +0,0 @@
{
"name": "@wrnexus/syntax",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/syntax — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/syntax"
},
"homepage": "https://wrnexusjs.dev/packages/syntax",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"syntax"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./parser": {
"types": "./dist/parser.d.ts",
"import": "./dist/parser.js"
},
"./tokenizer": {
"types": "./dist/tokenizer.d.ts",
"import": "./dist/tokenizer.js"
},
"./types": {
"types": "./dist/types.d.ts",
"import": "./dist/types.js"
},
"./diagnostics": {
"types": "./dist/diagnostics.d.ts",
"import": "./dist/diagnostics.js"
},
"./spec": {
"types": "./dist/spec.d.ts",
"import": "./dist/spec.js"
},
"./formatter": {
"types": "./dist/formatter.d.ts",
"import": "./dist/formatter.js"
}
},
"files": [
"dist",
"README.md"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.8.8",
"version": "0.8.9",
"type": "module",
"description": "@wrnexus/test — part of the WrNexus framework.",
"license": "MIT",
-128
View File
@@ -1,128 +0,0 @@
# @wrnexus/tracking
> Error tracking for WrNexus apps: capture exceptions manually or via middleware and fan them out to pluggable sinks.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
## Overview
`@wrnexus/tracking` is a small, server-side error-capture layer. You create a
tracker with one or more **sinks**, then feed it errors — either manually with
`tracker.capture(err, context)` or automatically by mounting `tracker.middleware()`
in your request pipeline. A `consoleSink` is included; forwarding to Sentry,
Datadog, or any other backend is just a matter of writing a tiny sink. Reach for
it when you want a single, sink-agnostic place to route application errors. Sinks
run best-effort — a throwing sink never breaks the request.
## Installation
```bash
bun add @wrnexus/tracking
```
> Private package — the machine must be authenticated to the `wrnexus` npm org
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
## API
### `createTracker(options?): Tracker`
Creates a tracker. `TrackerOptions`:
| Option | Type | Description |
| ------------ | ------------------------------------------- | --------------------------------------------------------------------------- |
| `sinks` | `ErrorSink[]` | Initial sinks to fan events out to. Defaults to `[]`. |
| `now` | `() => number` | Clock used for `event.timestamp` (epoch ms). Defaults to `Date.now`. |
| `beforeSend` | `(event: ErrorEvent) => ErrorEvent \| null` | Scrub/enrich an event before it reaches any sink. Return `null` to drop it. |
The returned `Tracker`:
| Member | Signature | Description |
| ------------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `capture` | `(error: unknown, context?: Record<string, unknown>) => Promise<void>` | Normalizes any thrown value into an `Error`, builds an `ErrorEvent`, runs `beforeSend`, then dispatches to all sinks. Non-`Error` values are wrapped in an `Error` named `NonError`. |
| `addSink` | `(sink: ErrorSink) => void` | Registers an additional sink at runtime. |
| `middleware` | `() => Middleware` | Returns a WrNexus `Middleware` that captures any error thrown downstream, then re-throws it so the framework's error handler still produces the response. |
The middleware attaches this context to captured events:
```ts
{ method: ctx.req.method, path: ctx.url.pathname, requestId: ctx.locals.requestId }
```
### `consoleSink: ErrorSink`
A built-in sink that logs a compact one-line message via `console.error`, e.g.
`[error] TypeError: cannot read x {"userId":42}`.
### Types
```ts
interface ErrorEvent {
error: Error;
context: Record<string, unknown>; // request info, user id, tags…
timestamp: number; // epoch ms
}
interface ErrorSink {
name?: string;
capture(event: ErrorEvent): void | Promise<void>;
}
```
## Usage
Manual capture:
```ts
import { createTracker, consoleSink } from "@wrnexus/tracking";
const tracker = createTracker({ sinks: [consoleSink] });
try {
await doWork();
} catch (err) {
await tracker.capture(err, { userId: 42, op: "doWork" });
throw err;
}
```
As request middleware:
```ts
import { createTracker, consoleSink } from "@wrnexus/tracking";
const tracker = createTracker({ sinks: [consoleSink] });
app.use(tracker.middleware()); // captures + re-throws downstream errors
```
A custom sink with `beforeSend` scrubbing:
```ts
import { createTracker, type ErrorSink } from "@wrnexus/tracking";
const sentrySink: ErrorSink = {
name: "sentry",
async capture(event) {
await Sentry.captureException(event.error, { extra: event.context });
},
};
const tracker = createTracker({
sinks: [sentrySink],
beforeSend(event) {
delete event.context.password; // scrub secrets
return event; // return null to drop the event entirely
},
});
tracker.addSink(anotherSink); // add more sinks later
```
## Requirements / Notes
- Runs on **Bun** only (Node is not supported).
- Peer package: [`@wrnexus/core`](../core) — the `Context` and `Middleware` types
used by `tracker.middleware()` come from there.
- Sink dispatch is fire-and-forget-safe: all sinks run via `Promise.all`, and a
sink that throws is swallowed so it can never break the app.
-43
View File
@@ -1,43 +0,0 @@
{
"name": "@wrnexus/tracking",
"version": "0.8.8",
"type": "module",
"description": "@wrnexus/tracking — part of the WrNexus framework.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://git.workroot.in/WorkRoot/WRNexusJS.git",
"directory": "packages/tracking"
},
"homepage": "https://wrnexusjs.dev/packages/tracking",
"bugs": {
"url": "https://git.workroot.in/WorkRoot/WRNexusJS/issues"
},
"keywords": [
"wrnexus",
"bun",
"typescript",
"tracking"
],
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "restricted"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist",
"README.md"
]
}
+35 -35
View File
@@ -1,6 +1,6 @@
# @wrnexus/ui
> First-party Wire UI component library — a set of themeable `.wrn` components plus a single tokenized stylesheet.
> First-party WrNexus UI component library — a set of themeable `.wrn` components plus a single tokenized stylesheet.
Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web framework.
@@ -11,7 +11,7 @@ controls, and feedback UI) together with one themeable stylesheet, `ui.css`. The
components are **auto-discovered** by the framework router — you don't import them
in code. Once the package's component directory is on the router's scan path, you
mount any component in a page with `data-component="<name>"`. Every visual is
driven by `var(--wire-*)` theme tokens, so components restyle instantly when the
driven by `var(--wrn-*)` theme tokens, so components restyle instantly when the
theme changes. The tiny JS surface (`src/index.ts`) exists only so the toolchain
(CLI build + dev server) can locate the component directory and stylesheet.
@@ -30,7 +30,7 @@ bun add @wrnexus/ui
> (a read token in `~/.npmrc`). Requires **Bun** (Node is not supported).
In practice you rarely install this directly: `@wrnexus/cli` and
`@wrnexus/dev-server` already depend on it and wire it into the router for you
`@wrnexus/dev-server` already depend on it and wrn it into the router for you
(see [Auto-discovery](#auto-discovery)).
## Components
@@ -54,19 +54,19 @@ element), and most render their body from either a named prop or the default slo
### Core / feedback
| Name | Purpose | Key props |
| -------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `button` | Button | `label`, `variant` (`default`\|`primary`\|`danger`\|`ghost`), `size` (`sm`\|`md`\|`lg`), `type` |
| `input` | Text input | see source |
| `textarea` | Multi-line input | see source |
| `checkbox` | Checkbox | see source |
| `badge` | Small status badge | `label`, `variant` |
| `alert` | Callout box | `variant` (`info`\|`success`\|`danger`\|`warning`), `title`, `message` |
| `card` | Padded, bordered surface | `class` |
| `avatar` | User avatar | see source |
| `spinner` | Loading indicator | see source |
| `disclosure` | Expandable details/summary | see source |
| `theme-toggle` | Theme switch button (binds `data-wire-theme-toggle`) | `label` |
| Name | Purpose | Key props |
| -------------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `button` | Button | `label`, `variant` (`default`\|`primary`\|`danger`\|`ghost`), `size` (`sm`\|`md`\|`lg`), `type` |
| `input` | Text input | see source |
| `textarea` | Multi-line input | see source |
| `checkbox` | Checkbox | see source |
| `badge` | Small status badge | `label`, `variant` |
| `alert` | Callout box | `variant` (`info`\|`success`\|`danger`\|`warning`), `title`, `message` |
| `card` | Padded, bordered surface | `class` |
| `avatar` | User avatar | see source |
| `spinner` | Loading indicator | see source |
| `disclosure` | Expandable details/summary | see source |
| `theme-toggle` | Theme switch button (binds `data-wrn-theme-toggle`) | `label` |
### Additional controls & data display
@@ -100,7 +100,7 @@ are `.wrn` files rendered server-side.
| ------------------ | -------------------------- | ------------------------------------------------------------------------------------------ |
| `uiComponentsDir` | `() => string` | Absolute path to the `.wrn` component directory (feed to `buildRouter`'s `componentDirs`). |
| `uiCssPath` | `() => string` | Absolute path to `ui.css`. |
| `uiCss` | `() => string` | The `ui.css` file contents (all `.wire-*` classes, themed via tokens). |
| `uiCss` | `() => string` | The `ui.css` file contents (all `.wrn-*` classes, themed via tokens). |
| `uiComponentNames` | `() => string[]` | Sorted list of declared built-in component names. |
| `uiComponentPath` | `(name: string) => string` | Absolute source path for a declared component name or case-insensitive alias. |
@@ -126,9 +126,9 @@ primitives. Applications can still use Tailwind independently in their own
source files.
The shared stylesheet gives all component boundaries consistent, GPU-friendly
entry and interaction motion. Override `--wire-motion-fast`,
`--wire-motion-base`, `--wire-motion-slow`, `--wire-ease-standard`, or
`--wire-ease-emphasized` to tune it. Hover lift is limited to precise pointing
entry and interaction motion. Override `--wrn-motion-fast`,
`--wrn-motion-base`, `--wrn-motion-slow`, `--wrn-ease-standard`, or
`--wrn-ease-emphasized` to tune it. Hover lift is limited to precise pointing
devices and `prefers-reduced-motion` is honored automatically.
### Using the selected theme in application UI
@@ -139,21 +139,21 @@ pages and custom `.wrn` components can use the same contract:
```css
.account-card {
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
border: 1px solid var(--wrn-color-border);
}
.account-card__action {
background: var(--wire-color-primary);
color: var(--wire-color-primary-contrast);
background: var(--wrn-color-primary);
color: var(--wrn-color-primary-contrast);
}
```
Stable no-spacing helper classes are also available: `wire-bg-page`,
`wire-bg-surface`, `wire-bg-surface-2`, `wire-bg-primary`, `wire-bg-secondary`,
`wire-text`, `wire-text-muted`, `wire-text-primary`, `wire-text-success`,
`wire-text-warning`, `wire-text-danger`, and `wire-border`.
Stable no-spacing helper classes are also available: `wrn-bg-page`,
`wrn-bg-surface`, `wrn-bg-surface-2`, `wrn-bg-primary`, `wrn-bg-secondary`,
`wrn-text`, `wrn-text-muted`, `wrn-text-primary`, `wrn-text-success`,
`wrn-text-warning`, `wrn-text-danger`, and `wrn-border`.
Tailwind-authored custom markup can continue using the palette families already
used by packaged components. `indigo-*` and `violet-*` resolve to primary,
@@ -169,7 +169,7 @@ The router scans extra `componentDirs` (in addition to the app's own
`app/components`) and keys components by name. Library dirs are scanned **first**
and `app/components` **last**, so an app component of the same name shadows the
library's. The CLI build (`@wrnexus/cli`) and dev server (`@wrnexus/dev-server`)
both wire the UI directory in for you:
both wrn the UI directory in for you:
```ts
import { buildRouter } from "@wrnexus/router";
@@ -195,11 +195,11 @@ attributes (other than `data-component`) become string props:
Ways to customize the components, in increasing order of power:
1. **Theme tokens** — override CSS custom properties such as `--wire-color-primary`,
`--wire-color-surface`, `--wire-radius-sm`, etc. Every component style resolves
through `var(--wire-*)`, so changing a token restyles everything instantly
1. **Theme tokens** — override CSS custom properties such as `--wrn-color-primary`,
`--wrn-color-surface`, `--wrn-radius-sm`, etc. Every component style resolves
through `var(--wrn-*)`, so changing a token restyles everything instantly
(including across theme switches).
2. **App CSS** — redefine a `.wire-*` class in your own stylesheet, which is loaded
2. **App CSS** — redefine a `.wrn-*` class in your own stylesheet, which is loaded
after `ui.css` and therefore wins.
3. **`class` prop** — pass a `class` prop to a component; it is appended to the
component's root element, letting you add per-instance classes without touching
@@ -217,4 +217,4 @@ Ways to customize the components, in increasing order of power:
[`@wrnexus/dev-server`](../dev-server) / built by [`@wrnexus/cli`](../cli).
- Depends on [`@wrnexus/core`](../core) (`dependencies`).
- `theme-toggle` relies on the framework's theme runtime, which binds the
`data-wire-theme-toggle` attribute — no per-component JS is required.
`data-wrn-theme-toggle` attribute — no per-component JS is required.
+150 -84
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Accordion {
outputs {
change(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)
@@ -105,15 +103,15 @@ component Accordion {
data-variant="{variant}"
data-indicator="{indicator}"
data-multiple="{allowsMultiple() ? 'true' : 'false'}"
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--accordion wire-next--accordion-{variant} {bordered ? 'wire-next--accordion-bordered' : ''} {separated ? 'wire-next--accordion-separated' : ''} {flush ? 'wire-next--accordion-flush' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--accordion wrn-next--accordion-{variant} {bordered ? 'wrn-next--accordion-bordered' : ''} {separated ? 'wrn-next--accordion-separated' : ''} {flush ? 'wrn-next--accordion-flush' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}"
>
{#each items as item, index}
<section
class="wire-next__accordion-item"
class="wrn-next__accordion-item"
data-open="{isOpen(itemValue(item, index)) ? 'true' : 'false'}"
data-disabled="{disabled || item.disabled ? 'true' : 'false'}"
>
<h3 class="wire-next__accordion-heading">
<h3 class="wrn-next__accordion-heading">
<button
type="button"
id="{id}-trigger-{index}"
@@ -123,7 +121,7 @@ component Accordion {
@click="toggleItem(event, itemValue(item, index), item)"
>
{#if showIndicator && indicatorPosition === "start"}
<span class="wire-next__accordion-indicator" aria-hidden="true">
<span class="wrn-next__accordion-indicator" aria-hidden="true">
{#if indicator === "chevron"}
<span class="icon-[lucide--chevron-down]"></span>
{:else}
@@ -133,7 +131,7 @@ component Accordion {
{/if}
<span>{item.label || item.title}</span>
{#if showIndicator && indicatorPosition === "end"}
<span class="wire-next__accordion-indicator" aria-hidden="true">
<span class="wrn-next__accordion-indicator" aria-hidden="true">
{#if indicator === "chevron"}
<span class="icon-[lucide--chevron-down]"></span>
{:else}
@@ -146,23 +144,23 @@ component Accordion {
<div
id="{id}-panel-{index}"
class="wire-next__accordion-panel"
class="wrn-next__accordion-panel"
role="region"
aria-labelledby="{id}-trigger-{index}"
aria-hidden="{isOpen(itemValue(item, index)) ? 'false' : 'true'}"
>
<div>
<div class="wire-next__accordion-content {contentItalic ? 'wire-next__accordion-content-italic' : ''}">
<div class="wrn-next__accordion-content {contentItalic ? 'wrn-next__accordion-content-italic' : ''}">
{#if item.content}<p>{item.content}</p>{/if}
{#if item.children && item.children.length > 0}
<div class="wire-next__accordion-nested">
<div class="wrn-next__accordion-nested">
{#each item.children as child, childIndex}
<section
class="wire-next__accordion-item"
class="wrn-next__accordion-item"
data-open="{isOpen(nestedValue(item, index, child, childIndex)) ? 'true' : 'false'}"
>
<h4 class="wire-next__accordion-heading">
<h4 class="wrn-next__accordion-heading">
<button
type="button"
id="{id}-trigger-{index}-{childIndex}"
@@ -172,7 +170,7 @@ component Accordion {
@click="toggleItem(event, nestedValue(item, index, child, childIndex), child)"
>
{#if showIndicator}
<span class="wire-next__accordion-indicator" aria-hidden="true">
<span class="wrn-next__accordion-indicator" aria-hidden="true">
{#if indicator === "chevron"}
<span class="icon-[lucide--chevron-down]"></span>
{:else}
@@ -185,13 +183,13 @@ component Accordion {
</h4>
<div
id="{id}-panel-{index}-{childIndex}"
class="wire-next__accordion-panel"
class="wrn-next__accordion-panel"
role="region"
aria-labelledby="{id}-trigger-{index}-{childIndex}"
aria-hidden="{isOpen(nestedValue(item, index, child, childIndex)) ? 'false' : 'true'}"
>
<div>
<div class="wire-next__accordion-content {contentItalic ? 'wire-next__accordion-content-italic' : ''}">
<div class="wrn-next__accordion-content {contentItalic ? 'wrn-next__accordion-content-italic' : ''}">
<p>{child.content}</p>
</div>
</div>
@@ -206,27 +204,26 @@ component Accordion {
</section>
{/each}
</div>
<ComponentBaseStyles hidden />
}
style {
.wire-next--accordion {
--wire-accordion-accent: var(--wire-component-color);
.wrn-next--accordion {
--wrn-accordion-accent: var(--wrn-component-color);
display: grid;
width: 100%;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-next__accordion-item {
.wrn-next__accordion-item {
min-width: 0;
}
.wire-next__accordion-heading {
.wrn-next__accordion-heading {
margin: 0;
font: inherit;
}
.wire-next__accordion-heading > button {
.wrn-next__accordion-heading > button {
display: flex;
width: 100%;
align-items: center;
@@ -234,7 +231,7 @@ component Accordion {
padding: 0.85rem 0;
border: 0;
background: transparent;
color: var(--wire-color-text);
color: var(--wrn-color-text);
font: inherit;
font-size: 0.88rem;
font-weight: 700;
@@ -242,171 +239,240 @@ component Accordion {
cursor: pointer;
}
.wire-next__accordion-heading > button > span:not(.wire-next__accordion-indicator) {
.wrn-next__accordion-heading > button > span:not(.wrn-next__accordion-indicator) {
min-width: 0;
flex: 1;
}
.wire-next__accordion-heading > button:focus-visible {
border-radius: var(--wire-radius-sm);
outline: 2px solid var(--wire-accordion-accent);
.wrn-next__accordion-heading > button:focus-visible {
border-radius: var(--wrn-radius-sm);
outline: 2px solid var(--wrn-accordion-accent);
outline-offset: 2px;
}
.wire-next__accordion-heading > button:disabled {
.wrn-next__accordion-heading > button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.wire-next__accordion-indicator {
.wrn-next__accordion-indicator {
display: inline-flex;
width: 1rem;
height: 1rem;
flex: 0 0 1rem;
align-items: center;
justify-content: center;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 1rem;
font-weight: 500;
line-height: 1;
transition:
color var(--wire-motion-fast) var(--wire-ease-standard),
transform var(--wire-motion-fast) var(--wire-ease-standard);
color var(--wrn-motion-fast) var(--wrn-ease-standard),
transform var(--wrn-motion-fast) var(--wrn-ease-standard);
}
.wire-next__accordion-indicator > span {
.wrn-next__accordion-indicator > span {
display: inline-flex;
align-items: center;
justify-content: center;
}
.wire-next__accordion-indicator > [class*="icon-["] {
.wrn-next__accordion-indicator > [class*="icon-["] {
width: 0.95rem;
height: 0.95rem;
}
.wire-next__accordion-item[data-open="true"]
> .wire-next__accordion-heading
.wire-next__accordion-indicator {
color: var(--wire-accordion-accent);
.wrn-next__accordion-item[data-open="true"]
> .wrn-next__accordion-heading
.wrn-next__accordion-indicator {
color: var(--wrn-accordion-accent);
transform: rotate(45deg);
}
.wire-next--accordion[data-indicator="chevron"]
.wire-next__accordion-item[data-open="true"]
> .wire-next__accordion-heading
.wire-next__accordion-indicator {
.wrn-next--accordion[data-indicator="chevron"]
.wrn-next__accordion-item[data-open="true"]
> .wrn-next__accordion-heading
.wrn-next__accordion-indicator {
transform: rotate(180deg);
}
.wire-next__accordion-item[data-open="true"] > .wire-next__accordion-heading > button {
color: var(--wire-accordion-accent);
.wrn-next__accordion-item[data-open="true"] > .wrn-next__accordion-heading > button {
color: var(--wrn-accordion-accent);
}
.wire-next__accordion-panel {
.wrn-next__accordion-panel {
display: grid;
grid-template-rows: 0fr;
opacity: 0;
transition:
grid-template-rows var(--wire-motion-base) var(--wire-ease-standard),
opacity var(--wire-motion-fast) var(--wire-ease-standard);
grid-template-rows var(--wrn-motion-base) var(--wrn-ease-standard),
opacity var(--wrn-motion-fast) var(--wrn-ease-standard);
}
.wire-next__accordion-item[data-open="true"] > .wire-next__accordion-panel {
.wrn-next__accordion-item[data-open="true"] > .wrn-next__accordion-panel {
grid-template-rows: 1fr;
opacity: 1;
}
.wire-next__accordion-panel > div {
.wrn-next__accordion-panel > div {
overflow: hidden;
}
.wire-next__accordion-content {
.wrn-next__accordion-content {
padding: 0 0 1rem 1.75rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.86rem;
line-height: 1.65;
}
.wire-next__accordion-content > p {
.wrn-next__accordion-content > p {
color: inherit;
}
.wire-next__accordion-content-italic > p::first-line {
.wrn-next__accordion-content-italic > p::first-line {
font-style: italic;
}
.wire-next__accordion-nested {
.wrn-next__accordion-nested {
display: grid;
margin-top: 0.5rem;
}
.wire-next__accordion-nested .wire-next__accordion-content {
.wrn-next__accordion-nested .wrn-next__accordion-content {
padding-left: 1.5rem;
}
.wire-next--accordion-bordered {
.wrn-next--accordion-bordered {
overflow: hidden;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
}
.wire-next--accordion-bordered > .wire-next__accordion-item + .wire-next__accordion-item {
border-top: 1px solid var(--wire-color-border);
.wrn-next--accordion-bordered > .wrn-next__accordion-item + .wrn-next__accordion-item {
border-top: 1px solid var(--wrn-color-border);
}
.wire-next--accordion-bordered
> .wire-next__accordion-item
> .wire-next__accordion-heading
.wrn-next--accordion-bordered
> .wrn-next__accordion-item
> .wrn-next__accordion-heading
> button {
padding-inline: 1rem;
}
.wire-next--accordion-bordered
> .wire-next__accordion-item
> .wire-next__accordion-panel
.wire-next__accordion-content {
.wrn-next--accordion-bordered
> .wrn-next__accordion-item
> .wrn-next__accordion-panel
.wrn-next__accordion-content {
padding-inline: 1rem;
}
.wire-next--accordion-separated {
.wrn-next--accordion-separated {
gap: 0.5rem;
}
.wire-next--accordion-separated > .wire-next__accordion-item {
.wrn-next--accordion-separated > .wrn-next__accordion-item {
overflow: hidden;
border: 1px solid transparent;
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface-2);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface-2);
}
.wire-next--accordion-separated > .wire-next__accordion-item[data-open="true"] {
border-color: var(--wire-color-border);
.wrn-next--accordion-separated > .wrn-next__accordion-item[data-open="true"] {
border-color: var(--wrn-color-border);
}
.wire-next--accordion-separated
> .wire-next__accordion-item
> .wire-next__accordion-heading
.wrn-next--accordion-separated
> .wrn-next__accordion-item
> .wrn-next__accordion-heading
> button,
.wire-next--accordion-separated
> .wire-next__accordion-item
> .wire-next__accordion-panel
.wire-next__accordion-content {
.wrn-next--accordion-separated
> .wrn-next__accordion-item
> .wrn-next__accordion-panel
.wrn-next__accordion-content {
padding-inline: 1rem;
}
.wire-next--accordion-flush {
.wrn-next--accordion-flush {
border-radius: 0;
background: transparent;
box-shadow: none;
}
@media (prefers-reduced-motion: reduce) {
.wire-next__accordion-panel,
.wire-next__accordion-indicator {
.wrn-next__accordion-panel,
.wrn-next__accordion-indicator {
transition: none;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+110 -44
View File
@@ -1,7 +1,5 @@
import SelectStyles from "../styles/SelectStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component AdvancedSelect {
outputs {
search(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)
@@ -257,7 +255,7 @@ component AdvancedSelect {
view {
<div
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--advanced-select {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--advanced-select {open ? 'wrn-next--open' : ''} {fixed ? 'wrn-next--advanced-select-fixed' : ''} {invalid ? 'wrn-next--invalid' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}"
data-placement="{placement}"
data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}"
@@ -270,17 +268,17 @@ component AdvancedSelect {
data-wrn-select
@focusout="if (!event.currentTarget.contains(event.relatedTarget)) { open = false }"
>
<div class="wire-next__row">
<div class="wrn-next__row">
<label id="{name}-label" for="{name}-trigger">{label}</label>
<small data-show="showCounter" data-text="counterText()">{counterText()}</small>
</div>
<div class="wire-next__select-control">
<div class="wrn-next__select-control">
<button
{...attrs}
id="{name}-trigger"
type="button"
class="wire-next__select-trigger"
class="wrn-next__select-trigger"
role="combobox"
aria-haspopup="listbox"
aria-expanded="{open}"
@@ -291,7 +289,7 @@ component AdvancedSelect {
@click="toggle()"
@keydown="handleKeydown(event)"
>
<span class="wire-next__select-value">
<span class="wrn-next__select-value">
<span data-show="!multiple" data-text="triggerText()">{triggerText()}</span>
<span
data-show="multiple && selectedTemplate === 'count' && selectedCount() > 0"
@@ -302,7 +300,7 @@ component AdvancedSelect {
data-text="selectedText()"
>{selectedText()}</span>
<span
class="wire-next__select-tags"
class="wrn-next__select-tags"
aria-label="{selectedOptionsLabel}"
data-show="multiple && selectedTemplate !== 'count' && selectedTemplate !== 'text' && selectedCount() > 0"
>
@@ -310,25 +308,25 @@ component AdvancedSelect {
<span data-show="isSelected(option)">
{#if optionTemplate === "icon" && option.icon}<i class="{option.icon}" aria-hidden="true"></i>{/if}
{#if optionTemplate === "avatar" && option.avatar}<img src="{option.avatar}" alt="" />{/if}
{#if optionTemplate === "color" && option.color}<i class="wire-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{#if optionTemplate === "color" && option.color}<i class="wrn-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{option.label}
</span>
{/each}
</span>
<span data-show="multiple && selectedCount() === 0">
{#if placeholderIcon}<i class="{placeholderIcon}" aria-hidden="true"></i>{/if}
<span class="wire-next__placeholder">{placeholder}</span>
<span class="wrn-next__placeholder">{placeholder}</span>
</span>
</span>
<span
class="wire-next__select-chevron icon-[lucide--chevrons-up-down]"
class="wrn-next__select-chevron icon-[lucide--chevrons-up-down]"
aria-hidden="true"
></span>
</button>
<button
type="button"
class="wire-next__clear-select"
class="wrn-next__clear-select"
data-show="clearable && allowEmpty && selectedCount() > 0 && !disabled"
aria-label="{clearLabel}"
title="{clearLabel}"
@@ -339,14 +337,14 @@ component AdvancedSelect {
</div>
<div
class="wire-next__select-dropdown {fixed ? 'wire-next__select-dropdown--fixed' : ''}"
class="wrn-next__select-dropdown {fixed ? 'wrn-next__select-dropdown--fixed' : ''}"
data-show="open"
style="{open ? '' : 'display: none'}"
>
{#if searchable}
<label class="wire-next__select-search">
<span class="wire-visually-hidden">{searchPlaceholder}</span>
<span class="wire-next__search-icon" aria-hidden="true">⌕</span>
<label class="wrn-next__select-search">
<span class="wrn-visually-hidden">{searchPlaceholder}</span>
<span class="wrn-next__search-icon" aria-hidden="true">⌕</span>
<input
type="search"
value="{query}"
@@ -359,28 +357,28 @@ component AdvancedSelect {
{/if}
<div
class="wire-next__select-message"
class="wrn-next__select-message"
data-show="remote && query.length !== 0 && query.length < minSearchLength"
>Enter at least {minSearchLength} characters.</div>
<div class="wire-next__select-message" data-show="loading" role="status">
<i class="wire-spinner wire-spinner--inline" aria-hidden="true"></i>
<div class="wrn-next__select-message" data-show="loading" role="status">
<i class="wrn-spinner wrn-spinner--inline" aria-hidden="true"></i>
{loadingLabel}
</div>
<div
id="{name}-listbox"
class="wire-next__select-list"
class="wrn-next__select-list"
data-show="(!remote || (query.length === 0 && remoteAutoLoad) || query.length >= minSearchLength) && !loading"
role="listbox"
aria-multiselectable="{multiple}"
>
{#each groups as group}
{#if visibleOptions(group.options || []).length}
<div class="wire-next__option-group" role="group" aria-label="{group.label}">
<div class="wire-next__group-label">{group.label}</div>
<div class="wrn-next__option-group" role="group" aria-label="{group.label}">
<div class="wrn-next__group-label">{group.label}</div>
{#each group.options || [] as option}
<button
type="button"
class="wire-next__select-option {isSelected(option) ? 'wire-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wire-next__select-option--active' : ''}"
class="wrn-next__select-option {isSelected(option) ? 'wrn-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wrn-next__select-option--active' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
@@ -391,10 +389,10 @@ component AdvancedSelect {
>
{#if optionTemplate === "icon" && option.icon}<i class="{option.icon}" aria-hidden="true"></i>{/if}
{#if optionTemplate === "avatar" && option.avatar}<img src="{option.avatar}" alt="" />{/if}
{#if optionTemplate === "color" && option.color}<i class="wire-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{#if optionTemplate === "color" && option.color}<i class="wrn-next__color-dot" style="--option-color: {option.color}"></i>{/if}
<span><strong>{option.label}</strong>{#if option.description}<small>{option.description}</small>{/if}</span>
<i
class="wire-next__check icon-[lucide--check]"
class="wrn-next__check icon-[lucide--check]"
data-show="isSelected(option)"
aria-hidden="true"
></i>
@@ -407,7 +405,7 @@ component AdvancedSelect {
{#each options as option}
<button
type="button"
class="wire-next__select-option {isSelected(option) ? 'wire-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wire-next__select-option--active' : ''}"
class="wrn-next__select-option {isSelected(option) ? 'wrn-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wrn-next__select-option--active' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
@@ -418,10 +416,10 @@ component AdvancedSelect {
>
{#if optionTemplate === "icon" && option.icon}<i class="{option.icon}" aria-hidden="true"></i>{/if}
{#if optionTemplate === "avatar" && option.avatar}<img src="{option.avatar}" alt="" />{/if}
{#if optionTemplate === "color" && option.color}<i class="wire-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{#if optionTemplate === "color" && option.color}<i class="wrn-next__color-dot" style="--option-color: {option.color}"></i>{/if}
<span><strong>{option.label}</strong>{#if option.description}<small>{option.description}</small>{/if}</span>
<i
class="wire-next__check icon-[lucide--check]"
class="wrn-next__check icon-[lucide--check]"
data-show="isSelected(option)"
aria-hidden="true"
></i>
@@ -429,16 +427,16 @@ component AdvancedSelect {
{/each}
{#if !loading && !flatVisibleOptions().length}
<div class="wire-next__select-message">{emptyLabel}</div>
<div class="wrn-next__select-message">{emptyLabel}</div>
{/if}
</div>
{#if tags && query && !allOptions().some((option) => option.label.toLowerCase() === query.toLowerCase())}
<button type="button" class="wire-next__create-option">{createLabel} “{query}”</button>
<button type="button" class="wrn-next__create-option">{createLabel} “{query}”</button>
{/if}
<button
type="button"
class="wire-next__load-more"
class="wrn-next__load-more"
data-wrn-select-load-more
data-show="infinite && hasMore"
>{loadMoreLabel}</button>
@@ -455,68 +453,136 @@ component AdvancedSelect {
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
class="wrn-next__validation"
data-error="{name}"
>{validationMessage}</small>
</div>
<SelectStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__row {
.wrn-next__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.wire-next__select-search {
.wrn-next__select-search {
position: relative;
display: block;
}
.wire-next__select-search input {
.wrn-next__select-search input {
padding-left: 2.25em;
}
.wire-next__select-tags {
.wrn-next__select-tags {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.wire-next__select-tags > span {
.wrn-next__select-tags > span {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.3rem 0.55rem;
border-radius: 999px;
color: var(--wire-component-color);
background: color-mix(in srgb, var(--wire-component-color) 12%, transparent);
color: var(--wrn-component-color);
background: color-mix(in srgb, var(--wrn-component-color) 12%, transparent);
font-size: 0.75em;
font-weight: 700;
}
.wire-next__select-tags img {
.wrn-next__select-tags img {
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
object-fit: cover;
}
.wire-next__select-value {
.wrn-next__select-value {
display: flex;
min-width: 0;
align-items: center;
gap: 0.55em;
}
.wire-next__placeholder {
.wrn-next__placeholder {
overflow: hidden;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
text-overflow: ellipsis;
white-space: nowrap;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+152 -86
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component AvatarGroup {
outputs {
overflow(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -45,21 +43,21 @@ component AvatarGroup {
view {
<div
{...attrs}
class="wire-component wire-next--avatar-group {class}"
class="wrn-component wrn-next--avatar-group {class}"
data-wrn-avatar-group
data-layout="{layout}"
data-shape="{shape}"
data-size="{size}"
style="--wire-avatar-group-columns:{columns}; --wire-avatar-group-ring:{borderColor || 'var(--wire-color-bg)'}"
style="--wrn-avatar-group-columns:{columns}; --wrn-avatar-group-ring:{borderColor || 'var(--wrn-color-bg)'}"
role="group"
aria-label="Avatar group"
>
<div
class="wire-next__avatar-group-members"
class="wrn-next__avatar-group-members"
>
{#each visibleMembers() as item}
<span
class="wire-next__avatar-group-member"
class="wrn-next__avatar-group-member"
data-size="{item.size || size}"
data-shape="{item.shape || shape}"
data-color="{item.color || color}"
@@ -68,7 +66,7 @@ component AvatarGroup {
aria-label="{item.name || item.alt || item.initials || 'Group member'}"
>
<span
class="wire-next__avatar-group-avatar"
class="wrn-next__avatar-group-avatar"
>
{#if item.src}
<img
@@ -93,7 +91,7 @@ component AvatarGroup {
{#if showTooltips && (item.tooltip || item.name)}
<span
class="wire-next__avatar-group-tooltip"
class="wrn-next__avatar-group-tooltip"
role="tooltip"
>
{item.tooltip || item.name}
@@ -104,11 +102,11 @@ component AvatarGroup {
{#if hiddenMembers().length > 0}
<span
class="wire-next__avatar-group-overflow"
class="wrn-next__avatar-group-overflow"
>
<button
type="button"
class="wire-next__avatar-group-overflow-button"
class="wrn-next__avatar-group-overflow-button"
aria-label="{overflowLabel}"
aria-expanded="{overflowOpen ? 'true' : 'false'}"
@click="toggleOverflow(event)"
@@ -117,18 +115,18 @@ component AvatarGroup {
</button>
<span
class="wire-next__avatar-group-menu"
class="wrn-next__avatar-group-menu"
role="menu"
data-show="overflowOpen"
aria-hidden="{overflowOpen ? 'false' : 'true'}"
>
{#each hiddenMembers() as item}
<span
class="wire-next__avatar-group-menu-item"
class="wrn-next__avatar-group-menu-item"
role="menuitem"
>
<span
class="wire-next__avatar-group-menu-avatar"
class="wrn-next__avatar-group-menu-avatar"
>
{#if item.src}
<img
@@ -148,144 +146,143 @@ component AvatarGroup {
{/if}
</div>
</div>
<ComponentBaseStyles hidden />
}
style {
/* --- Avatar group -------------------------------------------------------- */
.wire-next--avatar-group {
--wire-avatar-group-size: 3rem;
.wrn-next--avatar-group {
--wrn-avatar-group-size: 3rem;
position: relative;
display: inline-flex;
width: fit-content;
max-width: 100%;
}
.wire-next--avatar-group[data-size="xs"] {
--wire-avatar-group-size: 1.75rem;
.wrn-next--avatar-group[data-size="xs"] {
--wrn-avatar-group-size: 1.75rem;
}
.wire-next--avatar-group[data-size="sm"] {
--wire-avatar-group-size: 2.25rem;
.wrn-next--avatar-group[data-size="sm"] {
--wrn-avatar-group-size: 2.25rem;
}
.wire-next--avatar-group[data-size="md"],
.wire-next--avatar-group[data-size="default"] {
--wire-avatar-group-size: 3rem;
.wrn-next--avatar-group[data-size="md"],
.wrn-next--avatar-group[data-size="default"] {
--wrn-avatar-group-size: 3rem;
}
.wire-next--avatar-group[data-size="lg"] {
--wire-avatar-group-size: 4rem;
.wrn-next--avatar-group[data-size="lg"] {
--wrn-avatar-group-size: 4rem;
}
.wire-next--avatar-group[data-size="xl"] {
--wire-avatar-group-size: 5rem;
.wrn-next--avatar-group[data-size="xl"] {
--wrn-avatar-group-size: 5rem;
}
.wire-next__avatar-group-members {
.wrn-next__avatar-group-members {
display: flex;
align-items: center;
}
.wire-next--avatar-group[data-layout="stack"] .wire-next__avatar-group-member,
.wire-next--avatar-group[data-layout="stack"] .wire-next__avatar-group-overflow {
margin-left: calc(var(--wire-avatar-group-size) * -0.22);
.wrn-next--avatar-group[data-layout="stack"] .wrn-next__avatar-group-member,
.wrn-next--avatar-group[data-layout="stack"] .wrn-next__avatar-group-overflow {
margin-left: calc(var(--wrn-avatar-group-size) * -0.22);
}
.wire-next--avatar-group[data-layout="stack"] :first-child {
.wrn-next--avatar-group[data-layout="stack"] :first-child {
margin-left: 0;
}
.wire-next--avatar-group[data-layout="grid"] .wire-next__avatar-group-members {
.wrn-next--avatar-group[data-layout="grid"] .wrn-next__avatar-group-members {
display: grid;
grid-template-columns: repeat(var(--wire-avatar-group-columns), var(--wire-avatar-group-size));
grid-template-columns: repeat(var(--wrn-avatar-group-columns), var(--wrn-avatar-group-size));
gap: 0.65rem;
}
.wire-next__avatar-group-member,
.wire-next__avatar-group-overflow {
--wire-avatar-group-color: var(--wire-color-primary);
.wrn-next__avatar-group-member,
.wrn-next__avatar-group-overflow {
--wrn-avatar-group-color: var(--wrn-color-primary);
position: relative;
display: inline-grid;
width: var(--wire-avatar-group-size);
height: var(--wire-avatar-group-size);
width: var(--wrn-avatar-group-size);
height: var(--wrn-avatar-group-size);
flex: none;
place-items: center;
border-radius: 999px;
outline: none;
transition:
z-index var(--wire-motion-fast),
transform var(--wire-motion-base) var(--wire-ease-emphasized);
z-index var(--wrn-motion-fast),
transform var(--wrn-motion-base) var(--wrn-ease-emphasized);
}
.wire-next__avatar-group-member[data-color="secondary"] {
--wire-avatar-group-color: #737373;
.wrn-next__avatar-group-member[data-color="secondary"] {
--wrn-avatar-group-color: #737373;
}
.wire-next__avatar-group-member[data-color="success"] {
--wire-avatar-group-color: var(--wire-color-success);
.wrn-next__avatar-group-member[data-color="success"] {
--wrn-avatar-group-color: var(--wrn-color-success);
}
.wire-next__avatar-group-member[data-color="info"] {
--wire-avatar-group-color: var(--wire-color-info);
.wrn-next__avatar-group-member[data-color="info"] {
--wrn-avatar-group-color: var(--wrn-color-info);
}
.wire-next__avatar-group-member[data-color="warning"] {
--wire-avatar-group-color: var(--wire-color-warning);
.wrn-next__avatar-group-member[data-color="warning"] {
--wrn-avatar-group-color: var(--wrn-color-warning);
}
.wire-next__avatar-group-member[data-color="danger"] {
--wire-avatar-group-color: var(--wire-color-danger);
.wrn-next__avatar-group-member[data-color="danger"] {
--wrn-avatar-group-color: var(--wrn-color-danger);
}
.wire-next__avatar-group-member[data-shape="rounded"] {
border-radius: calc(var(--wire-radius-sm, 0.5rem) * 0.72);
.wrn-next__avatar-group-member[data-shape="rounded"] {
border-radius: calc(var(--wrn-radius-sm, 0.5rem) * 0.72);
}
.wire-next__avatar-group-avatar,
.wire-next__avatar-group-overflow-button {
.wrn-next__avatar-group-avatar,
.wrn-next__avatar-group-overflow-button {
display: grid;
width: 100%;
height: 100%;
overflow: hidden;
place-items: center;
color: white;
border: 2px solid var(--wire-avatar-group-ring);
border: 2px solid var(--wrn-avatar-group-ring);
border-radius: inherit;
background: var(--wire-avatar-group-color, var(--wire-color-primary));
background: var(--wrn-avatar-group-color, var(--wrn-color-primary));
box-shadow: 0 5px 14px rgb(0 0 0 / 0.16);
font: inherit;
font-size: calc(var(--wire-avatar-group-size) * 0.3);
font-size: calc(var(--wrn-avatar-group-size) * 0.3);
font-weight: 800;
}
.wire-next__avatar-group-avatar img,
.wire-next__avatar-group-menu-avatar img {
.wrn-next__avatar-group-avatar img,
.wrn-next__avatar-group-menu-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.wire-next__avatar-group-overflow-button {
.wrn-next__avatar-group-overflow-button {
cursor: pointer;
color: var(--wire-color-text);
background: var(--wire-color-surface-2);
color: var(--wrn-color-text);
background: var(--wrn-color-surface-2);
}
.wire-next__avatar-group-member:hover,
.wire-next__avatar-group-member:focus-visible,
.wire-next__avatar-group-overflow:focus-within {
.wrn-next__avatar-group-member:hover,
.wrn-next__avatar-group-member:focus-visible,
.wrn-next__avatar-group-overflow:focus-within {
z-index: 5;
transform: translateY(-3px);
}
.wire-next__avatar-group-member:focus-visible,
.wire-next__avatar-group-overflow-button:focus-visible {
outline: 2px solid var(--wire-color-primary);
.wrn-next__avatar-group-member:focus-visible,
.wrn-next__avatar-group-overflow-button:focus-visible {
outline: 2px solid var(--wrn-color-primary);
outline-offset: 3px;
}
.wire-next__avatar-group-tooltip {
.wrn-next__avatar-group-tooltip {
position: absolute;
left: 50%;
bottom: calc(100% + 0.65rem);
@@ -293,9 +290,9 @@ component AvatarGroup {
width: max-content;
max-width: 14rem;
padding: 0.4rem 0.6rem;
color: var(--wire-color-bg);
color: var(--wrn-color-bg);
border-radius: 0.4rem;
background: var(--wire-color-text);
background: var(--wrn-color-text);
box-shadow: 0 8px 22px rgb(0 0 0 / 0.22);
font-size: 0.75rem;
font-weight: 650;
@@ -303,17 +300,17 @@ component AvatarGroup {
opacity: 0;
transform: translate(-50%, 0.25rem);
transition:
opacity var(--wire-motion-fast),
transform var(--wire-motion-fast);
opacity var(--wrn-motion-fast),
transform var(--wrn-motion-fast);
}
.wire-next__avatar-group-member:hover .wire-next__avatar-group-tooltip,
.wire-next__avatar-group-member:focus-visible .wire-next__avatar-group-tooltip {
.wrn-next__avatar-group-member:hover .wrn-next__avatar-group-tooltip,
.wrn-next__avatar-group-member:focus-visible .wrn-next__avatar-group-tooltip {
opacity: 1;
transform: translate(-50%, 0);
}
.wire-next__avatar-group-menu {
.wrn-next__avatar-group-menu {
position: absolute;
top: calc(100% + 0.65rem);
right: 0;
@@ -323,14 +320,14 @@ component AvatarGroup {
min-width: 12rem;
gap: 0.25rem;
padding: 0.45rem;
color: var(--wire-color-text);
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm, 0.5rem);
background: var(--wire-color-surface);
color: var(--wrn-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm, 0.5rem);
background: var(--wrn-color-surface);
box-shadow: 0 16px 38px rgb(0 0 0 / 0.22);
}
.wire-next__avatar-group-menu-item {
.wrn-next__avatar-group-menu-item {
display: flex;
align-items: center;
gap: 0.6rem;
@@ -339,11 +336,11 @@ component AvatarGroup {
font-size: 0.82rem;
}
.wire-next__avatar-group-menu-item:hover {
background: var(--wire-color-surface-2);
.wrn-next__avatar-group-menu-item:hover {
background: var(--wrn-color-surface-2);
}
.wire-next__avatar-group-menu-avatar {
.wrn-next__avatar-group-menu-avatar {
display: grid;
width: 1.75rem;
height: 1.75rem;
@@ -351,9 +348,78 @@ component AvatarGroup {
flex: none;
place-items: center;
border-radius: 999px;
background: var(--wire-color-surface-2);
background: var(--wrn-color-surface-2);
font-size: 0.65rem;
font-weight: 800;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+151 -85
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Blockquote {
props {
quote: string = "I just wanted to say that I'm very happy with my purchase so far. The documentation is outstanding - clear and detailed."
@@ -20,7 +18,7 @@ component Blockquote {
view {
<figure
{...attrs}
class="wire-component wire-next--blockquote wire-component--color-{color} wire-component--size-{size} {class}"
class="wrn-component wrn-next--blockquote wrn-component--color-{color} wrn-component--size-{size} {class}"
data-size="{size}"
data-color="{color}"
data-align="{align}"
@@ -29,10 +27,10 @@ component Blockquote {
>
<blockquote cite="{citationUrl}">
{#if quoteMark}
<span class="wire-next__blockquote-mark" aria-hidden="true">“</span>
<span class="wrn-next__blockquote-mark" aria-hidden="true">“</span>
{/if}
<div class="wire-next__blockquote-copy">
<div class="wrn-next__blockquote-copy">
{#if quote}
<p>{quote}</p>
{:else}
@@ -42,17 +40,17 @@ component Blockquote {
</blockquote>
{#if citation || citationTitle || avatarSrc}
<figcaption class="wire-next__blockquote-citation">
<figcaption class="wrn-next__blockquote-citation">
{#if avatarSrc}
<img
class="wire-next__blockquote-avatar"
class="wrn-next__blockquote-avatar"
src="{avatarSrc}"
alt="{avatarAlt}"
loading="lazy"
/>
{/if}
<span class="wire-next__blockquote-attribution">
<span class="wrn-next__blockquote-attribution">
{#if citation}
{#if citationUrl}
<cite><a href="{citationUrl}">{citation}</a></cite>
@@ -65,63 +63,62 @@ component Blockquote {
</figcaption>
{/if}
</figure>
<ComponentBaseStyles hidden />
}
style {
.wire-next--blockquote {
--wire-blockquote-color: var(--wire-component-color, var(--wire-color-primary));
--wire-blockquote-font-size: 1.125rem;
--wire-blockquote-mark-size: 4.25rem;
.wrn-next--blockquote {
--wrn-blockquote-color: var(--wrn-component-color, var(--wrn-color-primary));
--wrn-blockquote-font-size: 1.125rem;
--wrn-blockquote-mark-size: 4.25rem;
display: grid;
gap: 1.25rem;
width: 100%;
color: var(--wire-color-text);
color: var(--wrn-color-text);
text-align: left;
}
.wire-next--blockquote[data-size="xs"] {
--wire-blockquote-font-size: 0.875rem;
--wire-blockquote-mark-size: 3rem;
.wrn-next--blockquote[data-size="xs"] {
--wrn-blockquote-font-size: 0.875rem;
--wrn-blockquote-mark-size: 3rem;
}
.wire-next--blockquote[data-size="sm"] {
--wire-blockquote-font-size: 1rem;
--wire-blockquote-mark-size: 3.5rem;
.wrn-next--blockquote[data-size="sm"] {
--wrn-blockquote-font-size: 1rem;
--wrn-blockquote-mark-size: 3.5rem;
}
.wire-next--blockquote[data-size="md"],
.wire-next--blockquote[data-size="default"] {
--wire-blockquote-font-size: 1.125rem;
.wrn-next--blockquote[data-size="md"],
.wrn-next--blockquote[data-size="default"] {
--wrn-blockquote-font-size: 1.125rem;
}
.wire-next--blockquote[data-size="lg"] {
--wire-blockquote-font-size: clamp(1.35rem, 2.4vw, 1.75rem);
--wire-blockquote-mark-size: 5rem;
.wrn-next--blockquote[data-size="lg"] {
--wrn-blockquote-font-size: clamp(1.35rem, 2.4vw, 1.75rem);
--wrn-blockquote-mark-size: 5rem;
}
.wire-next--blockquote[data-size="xl"] {
--wire-blockquote-font-size: clamp(1.65rem, 3vw, 2.25rem);
--wire-blockquote-mark-size: 5.75rem;
.wrn-next--blockquote[data-size="xl"] {
--wrn-blockquote-font-size: clamp(1.65rem, 3vw, 2.25rem);
--wrn-blockquote-mark-size: 5.75rem;
}
.wire-next--blockquote > blockquote {
.wrn-next--blockquote > blockquote {
position: relative;
display: block;
min-width: 0;
margin: 0;
padding-top: calc(var(--wire-blockquote-mark-size) * 0.78);
padding-top: calc(var(--wrn-blockquote-mark-size) * 0.78);
}
.wire-next__blockquote-mark {
.wrn-next__blockquote-mark {
position: absolute;
top: 0;
inset-inline: 0;
display: block;
width: 100%;
color: color-mix(in srgb, var(--wire-blockquote-color) 28%, var(--wire-color-text));
color: color-mix(in srgb, var(--wrn-blockquote-color) 28%, var(--wrn-color-text));
font-family: Georgia, "Times New Roman", serif;
font-size: var(--wire-blockquote-mark-size);
font-size: var(--wrn-blockquote-mark-size);
font-style: normal;
font-weight: 700;
line-height: 0.8;
@@ -129,145 +126,214 @@ component Blockquote {
user-select: none;
}
.wire-next__blockquote-copy {
.wrn-next__blockquote-copy {
min-width: 0;
font-family: Georgia, "Times New Roman", serif;
font-size: var(--wire-blockquote-font-size);
font-size: var(--wrn-blockquote-font-size);
line-height: 1.55;
text-wrap: pretty;
}
.wire-next--blockquote[data-italic="true"] .wire-next__blockquote-copy {
.wrn-next--blockquote[data-italic="true"] .wrn-next__blockquote-copy {
font-style: italic;
}
.wire-next__blockquote-copy > :first-child {
.wrn-next__blockquote-copy > :first-child {
margin-top: 0;
}
.wire-next__blockquote-copy > :last-child {
.wrn-next__blockquote-copy > :last-child {
margin-bottom: 0;
}
.wire-next__blockquote-citation {
.wrn-next__blockquote-citation {
display: inline-flex;
gap: 0.75rem;
align-items: center;
width: fit-content;
margin-inline-start: 0;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.875rem;
line-height: 1.35;
}
.wire-next__blockquote-avatar {
.wrn-next__blockquote-avatar {
width: 2.5rem;
height: 2.5rem;
flex: 0 0 auto;
border: 2px solid color-mix(in srgb, var(--wire-blockquote-color) 35%, transparent);
border: 2px solid color-mix(in srgb, var(--wrn-blockquote-color) 35%, transparent);
border-radius: 999px;
object-fit: cover;
}
.wire-next__blockquote-attribution {
.wrn-next__blockquote-attribution {
display: grid;
gap: 0.15rem;
}
.wire-next__blockquote-attribution cite {
color: var(--wire-color-text);
.wrn-next__blockquote-attribution cite {
color: var(--wrn-color-text);
font-style: normal;
font-weight: 700;
}
.wire-next__blockquote-attribution a {
.wrn-next__blockquote-attribution a {
color: inherit;
text-decoration-color: color-mix(in srgb, var(--wire-blockquote-color) 55%, transparent);
text-decoration-color: color-mix(in srgb, var(--wrn-blockquote-color) 55%, transparent);
text-underline-offset: 0.2em;
}
.wire-next__blockquote-attribution a:hover {
color: var(--wire-blockquote-color);
.wrn-next__blockquote-attribution a:hover {
color: var(--wrn-blockquote-color);
}
.wire-next--blockquote[data-align="center"] {
.wrn-next--blockquote[data-align="center"] {
text-align: center;
}
.wire-next--blockquote[data-align="right"],
.wire-next--blockquote[data-align="end"] {
.wrn-next--blockquote[data-align="right"],
.wrn-next--blockquote[data-align="end"] {
text-align: right;
}
.wire-next--blockquote[data-align="center"] > blockquote,
.wire-next--blockquote[data-align="right"] > blockquote,
.wire-next--blockquote[data-align="end"] > blockquote {
.wrn-next--blockquote[data-align="center"] > blockquote,
.wrn-next--blockquote[data-align="right"] > blockquote,
.wrn-next--blockquote[data-align="end"] > blockquote {
text-align: inherit;
}
.wire-next--blockquote[data-align="center"] .wire-next__blockquote-mark {
.wrn-next--blockquote[data-align="center"] .wrn-next__blockquote-mark {
text-align: center;
}
.wire-next--blockquote[data-align="right"] .wire-next__blockquote-mark,
.wire-next--blockquote[data-align="end"] .wire-next__blockquote-mark {
.wrn-next--blockquote[data-align="right"] .wrn-next__blockquote-mark,
.wrn-next--blockquote[data-align="end"] .wrn-next__blockquote-mark {
text-align: end;
}
.wire-next--blockquote[data-align="center"] .wire-next__blockquote-citation {
.wrn-next--blockquote[data-align="center"] .wrn-next__blockquote-citation {
margin-inline: auto;
}
.wire-next--blockquote[data-align="right"] .wire-next__blockquote-citation,
.wire-next--blockquote[data-align="end"] .wire-next__blockquote-citation {
.wrn-next--blockquote[data-align="right"] .wrn-next__blockquote-citation,
.wrn-next--blockquote[data-align="end"] .wrn-next__blockquote-citation {
margin-inline-start: auto;
}
.wire-next--blockquote[data-variant="bordered"],
.wire-next--blockquote[data-variant="border"],
.wire-next--blockquote[data-variant="left-border"] {
.wrn-next--blockquote[data-variant="bordered"],
.wrn-next--blockquote[data-variant="border"],
.wrn-next--blockquote[data-variant="left-border"] {
padding-inline-start: 1.5rem;
border-inline-start: 4px solid var(--wire-blockquote-color);
border-inline-start: 4px solid var(--wrn-blockquote-color);
}
.wire-next--blockquote[data-variant="bordered"] .wire-next__blockquote-mark,
.wire-next--blockquote[data-variant="border"] .wire-next__blockquote-mark,
.wire-next--blockquote[data-variant="left-border"] .wire-next__blockquote-mark {
.wrn-next--blockquote[data-variant="bordered"] .wrn-next__blockquote-mark,
.wrn-next--blockquote[data-variant="border"] .wrn-next__blockquote-mark,
.wrn-next--blockquote[data-variant="left-border"] .wrn-next__blockquote-mark {
display: none;
}
.wire-next--blockquote[data-variant="bordered"] > blockquote,
.wire-next--blockquote[data-variant="border"] > blockquote,
.wire-next--blockquote[data-variant="left-border"] > blockquote {
.wrn-next--blockquote[data-variant="bordered"] > blockquote,
.wrn-next--blockquote[data-variant="border"] > blockquote,
.wrn-next--blockquote[data-variant="left-border"] > blockquote {
display: block;
padding-top: 0;
}
.wire-next--blockquote[data-variant="bordered"] .wire-next__blockquote-copy,
.wire-next--blockquote[data-variant="border"] .wire-next__blockquote-copy,
.wire-next--blockquote[data-variant="left-border"] .wire-next__blockquote-copy {
.wrn-next--blockquote[data-variant="bordered"] .wrn-next__blockquote-copy,
.wrn-next--blockquote[data-variant="border"] .wrn-next__blockquote-copy,
.wrn-next--blockquote[data-variant="left-border"] .wrn-next__blockquote-copy {
padding-top: 0;
}
.wire-next--blockquote[data-variant="bordered"] .wire-next__blockquote-citation,
.wire-next--blockquote[data-variant="border"] .wire-next__blockquote-citation,
.wire-next--blockquote[data-variant="left-border"] .wire-next__blockquote-citation {
.wrn-next--blockquote[data-variant="bordered"] .wrn-next__blockquote-citation,
.wrn-next--blockquote[data-variant="border"] .wrn-next__blockquote-citation,
.wrn-next--blockquote[data-variant="left-border"] .wrn-next__blockquote-citation {
margin-inline-start: 0;
}
@media (max-width: 480px) {
.wire-next--blockquote {
--wire-blockquote-mark-size: 3.25rem;
--wire-blockquote-font-size: 1rem;
.wrn-next--blockquote {
--wrn-blockquote-mark-size: 3.25rem;
--wrn-blockquote-font-size: 1rem;
gap: 1rem;
}
.wire-next--blockquote[data-size="lg"],
.wire-next--blockquote[data-size="xl"] {
--wire-blockquote-font-size: 1.25rem;
.wrn-next--blockquote[data-size="lg"],
.wrn-next--blockquote[data-size="xl"] {
--wrn-blockquote-font-size: 1.25rem;
}
.wire-next__blockquote-citation {
.wrn-next__blockquote-citation {
margin-inline-start: 0;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+87 -87
View File
@@ -26,34 +26,34 @@ label: string = "Breadcrumb"
data-color='{color}'
data-variant='{variant}'
aria-label='{label}'
class='wire-breadcrumb {class}'
class='wrn-breadcrumb {class}'
>
<ol class="wire-breadcrumb__list">
<ol class="wrn-breadcrumb__list">
{#if showHome}
<li class="wire-breadcrumb__item wire-breadcrumb__item--home">
<li class="wrn-breadcrumb__item wrn-breadcrumb__item--home">
<a
href='{homeHref || "/"}'
class="wire-breadcrumb__link wire-breadcrumb__home"
class="wrn-breadcrumb__link wrn-breadcrumb__home"
@click='output.select({ item: { label: homeLabel, href: homeHref || "/", value: "home" }, itemIndex: -1 })'
>
{#if homeIcon === "icon-[lucide--house]"}
<span class="icon-[lucide--house] wire-breadcrumb__icon" aria-hidden="true"></span>
<span class="icon-[lucide--house] wrn-breadcrumb__icon" aria-hidden="true"></span>
{:else if homeIcon}
<span class='{homeIcon} wire-breadcrumb__icon' aria-hidden="true"></span>
<span class='{homeIcon} wrn-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__home-label">{homeLabel}</span>
<span class="wrn-breadcrumb__home-label">{homeLabel}</span>
</a>
</li>
{/if}
{#each items as item, itemIndex}
<li
class="wire-breadcrumb__item"
class="wrn-breadcrumb__item"
data-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "true" : "false"}'
>
{#if showHome || itemIndex > 0}
<span class="wire-breadcrumb__separator" aria-hidden="true">
<span class="wrn-breadcrumb__separator" aria-hidden="true">
{#if separator === "slash"}
<span>/</span>
{:else if separator === "dot"}
@@ -61,7 +61,7 @@ label: string = "Breadcrumb"
{:else if separator === "arrow"}
<span>→</span>
{:else}
<span class="icon-[lucide--chevron-right] wire-breadcrumb__separator-icon"></span>
<span class="icon-[lucide--chevron-right] wrn-breadcrumb__separator-icon"></span>
{/if}
</span>
{/if}
@@ -71,30 +71,30 @@ label: string = "Breadcrumb"
href='{item.href}'
target='{item.target || ""}'
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
class="wire-breadcrumb__link"
class="wrn-breadcrumb__link"
@click='output.select({ item: item, itemIndex: itemIndex })'
>
{#if item.icon}
<span class='{item.icon} wire-breadcrumb__icon' aria-hidden="true"></span>
<span class='{item.icon} wrn-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__label">{item.label || item.title}</span>
<span class="wrn-breadcrumb__label">{item.label || item.title}</span>
{#if item.external}
<span class="icon-[lucide--arrow-up-right] wire-breadcrumb__external" aria-hidden="true"></span>
<span class="icon-[lucide--arrow-up-right] wrn-breadcrumb__external" aria-hidden="true"></span>
{/if}
</a>
{:else}
<span
class="wire-breadcrumb__current"
class="wrn-breadcrumb__current"
aria-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "page" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}'
>
{#if item.icon}
<span class='{item.icon} wire-breadcrumb__icon' aria-hidden="true"></span>
<span class='{item.icon} wrn-breadcrumb__icon' aria-hidden="true"></span>
{/if}
<span class="wire-breadcrumb__label">{item.label || item.title}</span>
<span class="wrn-breadcrumb__label">{item.label || item.title}</span>
</span>
{/if}
</li>
@@ -104,52 +104,52 @@ label: string = "Breadcrumb"
}
style {
.wire-breadcrumb {
--breadcrumb-accent: var(--wire-color-primary);
--breadcrumb-soft: var(--wire-color-primary-soft);
--breadcrumb-muted: var(--wire-color-primary-muted);
--breadcrumb-text: var(--wire-color-primary-text);
.wrn-breadcrumb {
--breadcrumb-accent: var(--wrn-color-primary);
--breadcrumb-soft: var(--wrn-color-primary-soft);
--breadcrumb-muted: var(--wrn-color-primary-muted);
--breadcrumb-text: var(--wrn-color-primary-text);
min-width: 0;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-breadcrumb[data-color="secondary"] {
--breadcrumb-accent: var(--wire-color-secondary);
--breadcrumb-soft: var(--wire-color-secondary-soft);
--breadcrumb-muted: var(--wire-color-secondary-muted);
--breadcrumb-text: var(--wire-color-secondary-text);
.wrn-breadcrumb[data-color="secondary"] {
--breadcrumb-accent: var(--wrn-color-secondary);
--breadcrumb-soft: var(--wrn-color-secondary-soft);
--breadcrumb-muted: var(--wrn-color-secondary-muted);
--breadcrumb-text: var(--wrn-color-secondary-text);
}
.wire-breadcrumb[data-color="info"] {
--breadcrumb-accent: var(--wire-color-info);
--breadcrumb-soft: var(--wire-color-info-soft);
--breadcrumb-muted: var(--wire-color-info-muted);
--breadcrumb-text: var(--wire-color-info-text);
.wrn-breadcrumb[data-color="info"] {
--breadcrumb-accent: var(--wrn-color-info);
--breadcrumb-soft: var(--wrn-color-info-soft);
--breadcrumb-muted: var(--wrn-color-info-muted);
--breadcrumb-text: var(--wrn-color-info-text);
}
.wire-breadcrumb[data-color="success"] {
--breadcrumb-accent: var(--wire-color-success);
--breadcrumb-soft: var(--wire-color-success-soft);
--breadcrumb-muted: var(--wire-color-success-muted);
--breadcrumb-text: var(--wire-color-success-text);
.wrn-breadcrumb[data-color="success"] {
--breadcrumb-accent: var(--wrn-color-success);
--breadcrumb-soft: var(--wrn-color-success-soft);
--breadcrumb-muted: var(--wrn-color-success-muted);
--breadcrumb-text: var(--wrn-color-success-text);
}
.wire-breadcrumb[data-color="warning"] {
--breadcrumb-accent: var(--wire-color-warning);
--breadcrumb-soft: var(--wire-color-warning-soft);
--breadcrumb-muted: var(--wire-color-warning-muted);
--breadcrumb-text: var(--wire-color-warning-text);
.wrn-breadcrumb[data-color="warning"] {
--breadcrumb-accent: var(--wrn-color-warning);
--breadcrumb-soft: var(--wrn-color-warning-soft);
--breadcrumb-muted: var(--wrn-color-warning-muted);
--breadcrumb-text: var(--wrn-color-warning-text);
}
.wire-breadcrumb[data-color="danger"] {
--breadcrumb-accent: var(--wire-color-danger);
--breadcrumb-soft: var(--wire-color-danger-soft);
--breadcrumb-muted: var(--wire-color-danger-muted);
--breadcrumb-text: var(--wire-color-danger-text);
.wrn-breadcrumb[data-color="danger"] {
--breadcrumb-accent: var(--wrn-color-danger);
--breadcrumb-soft: var(--wrn-color-danger-soft);
--breadcrumb-muted: var(--wrn-color-danger-muted);
--breadcrumb-text: var(--wrn-color-danger-text);
}
.wire-breadcrumb[data-variant="soft"] {
.wrn-breadcrumb[data-variant="soft"] {
width: fit-content;
max-width: 100%;
padding: 0.45rem 0.65rem;
@@ -158,21 +158,21 @@ label: string = "Breadcrumb"
border-radius: 0.75rem;
}
.wire-breadcrumb[data-variant="outline"] {
.wrn-breadcrumb[data-variant="outline"] {
width: fit-content;
max-width: 100%;
padding: 0.45rem 0.65rem;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
background: var(--wrn-color-surface-raised);
border: 1px solid var(--wrn-color-border);
border-radius: 0.75rem;
box-shadow: var(--wire-shadow-sm);
box-shadow: var(--wrn-shadow-sm);
}
.wire-breadcrumb[data-variant="contrast"] {
.wrn-breadcrumb[data-variant="contrast"] {
color: rgba(255, 255, 255, 0.78);
}
.wire-breadcrumb__list {
.wrn-breadcrumb__list {
display: flex;
align-items: center;
gap: 0;
@@ -185,34 +185,34 @@ label: string = "Breadcrumb"
white-space: nowrap;
}
.wire-breadcrumb__list::-webkit-scrollbar {
.wrn-breadcrumb__list::-webkit-scrollbar {
display: none;
}
.wire-breadcrumb__item {
.wrn-breadcrumb__item {
display: inline-flex;
align-items: center;
min-width: 0;
flex: 0 0 auto;
}
.wire-breadcrumb__separator {
.wrn-breadcrumb__separator {
display: inline-flex;
align-items: center;
justify-content: center;
margin-inline: 0.45rem;
color: var(--wire-color-text-subtle);
color: var(--wrn-color-text-subtle);
font-size: 0.75rem;
opacity: 0.72;
}
.wire-breadcrumb__separator-icon {
.wrn-breadcrumb__separator-icon {
width: 0.9rem;
height: 0.9rem;
}
.wire-breadcrumb__link,
.wire-breadcrumb__current {
.wrn-breadcrumb__link,
.wrn-breadcrumb__current {
display: inline-flex;
align-items: center;
gap: 0.4rem;
@@ -225,97 +225,97 @@ label: string = "Breadcrumb"
text-decoration: none;
}
.wire-breadcrumb[data-size="sm"] .wire-breadcrumb__link,
.wire-breadcrumb[data-size="sm"] .wire-breadcrumb__current {
.wrn-breadcrumb[data-size="sm"] .wrn-breadcrumb__link,
.wrn-breadcrumb[data-size="sm"] .wrn-breadcrumb__current {
min-height: 1.75rem;
font-size: 0.75rem;
}
.wire-breadcrumb[data-size="lg"] .wire-breadcrumb__link,
.wire-breadcrumb[data-size="lg"] .wire-breadcrumb__current {
.wrn-breadcrumb[data-size="lg"] .wrn-breadcrumb__link,
.wrn-breadcrumb[data-size="lg"] .wrn-breadcrumb__current {
min-height: 2.25rem;
font-size: 0.875rem;
}
.wire-breadcrumb__link {
color: var(--wire-color-text-muted);
.wrn-breadcrumb__link {
color: var(--wrn-color-text-muted);
font-weight: 600;
transition:
color 160ms ease,
background 160ms ease;
}
.wire-breadcrumb__link:hover {
.wrn-breadcrumb__link:hover {
color: var(--breadcrumb-accent);
background: var(--breadcrumb-soft);
}
.wire-breadcrumb__link:focus-visible {
.wrn-breadcrumb__link:focus-visible {
color: var(--breadcrumb-accent);
outline: 2px solid var(--breadcrumb-accent);
outline-offset: 2px;
}
.wire-breadcrumb__current {
.wrn-breadcrumb__current {
max-width: 22rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
font-weight: 750;
}
.wire-breadcrumb__item[data-current="true"] .wire-breadcrumb__current {
.wrn-breadcrumb__item[data-current="true"] .wrn-breadcrumb__current {
color: var(--breadcrumb-text);
background: var(--breadcrumb-soft);
}
.wire-breadcrumb__current[aria-disabled="true"] {
.wrn-breadcrumb__current[aria-disabled="true"] {
opacity: 0.58;
}
.wire-breadcrumb__icon,
.wire-breadcrumb__external {
.wrn-breadcrumb__icon,
.wrn-breadcrumb__external {
flex: 0 0 auto;
width: 0.95rem;
height: 0.95rem;
}
.wire-breadcrumb__home {
.wrn-breadcrumb__home {
color: var(--breadcrumb-accent);
}
.wire-breadcrumb__label {
.wrn-breadcrumb__label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__link,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__current,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__home {
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__link,
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__current,
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__home {
color: inherit;
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__link:hover,
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__item[data-current="true"] .wire-breadcrumb__current {
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__link:hover,
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__item[data-current="true"] .wrn-breadcrumb__current {
color: #ffffff;
background: rgba(255, 255, 255, 0.12);
}
.wire-breadcrumb[data-variant="contrast"] .wire-breadcrumb__separator {
.wrn-breadcrumb[data-variant="contrast"] .wrn-breadcrumb__separator {
color: rgba(255, 255, 255, 0.62);
}
@media (max-width: 639px) {
.wire-breadcrumb__home-label {
.wrn-breadcrumb__home-label {
display: none;
}
.wire-breadcrumb__current {
.wrn-breadcrumb__current {
max-width: 13rem;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-breadcrumb__link {
.wrn-breadcrumb__link {
transition: none;
}
}
+141 -76
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ButtonGroup {
outputs {
click(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -56,7 +54,7 @@ items: unknown[] = []
view {
<div
{...attrs}
class="wire-component wire-next--button-group {class}"
class="wrn-component wrn-next--button-group {class}"
role="{toolbar ? 'toolbar' : 'group'}"
aria-label="{ariaLabel}"
aria-orientation="{orientation}"
@@ -73,7 +71,7 @@ items: unknown[] = []
{#each items as item, index}
<button
type="{item.type || 'button'}"
class="wire-btn wire-btn--variant-{item.variant || variant} wire-btn--color-{item.color || color} wire-btn--size-{item.size || size}"
class="wrn-btn wrn-btn--variant-{item.variant || variant} wrn-btn--color-{item.color || color} wrn-btn--size-{item.size || size}"
disabled="{disabled || item.disabled}"
aria-label="{item.ariaLabel || item.label}"
aria-pressed="{selectable ? (selectedValue === (item.value || item.label || String(index)) ? 'true' : 'false') : ''}"
@@ -82,19 +80,18 @@ items: unknown[] = []
@click="selectItem(item, index)"
>
{#if item.icon}
<span class="wire-btn__icon {item.icon}" aria-hidden="true"></span>
<span class="wrn-btn__icon {item.icon}" aria-hidden="true"></span>
{/if}
<span class="wire-btn__label">{item.label}</span>
<span class="wrn-btn__label">{item.label}</span>
</button>
{/each}
{/if}
<slot />
</div>
<ComponentBaseStyles hidden />
}
style {
.wire-next--button-group {
.wrn-next--button-group {
display: inline-flex;
align-items: stretch;
width: fit-content;
@@ -102,138 +99,206 @@ items: unknown[] = []
isolation: isolate;
}
.wire-next--button-group[data-orientation="vertical"] {
.wrn-next--button-group[data-orientation="vertical"] {
flex-direction: column;
}
.wire-next--button-group[data-attached="false"] {
.wrn-next--button-group[data-attached="false"] {
gap: 0.5rem;
}
.wire-next--button-group[data-disabled="true"] {
.wrn-next--button-group[data-disabled="true"] {
cursor: not-allowed;
opacity: 0.6;
}
.wire-next--button-group[data-attached="true"] > :is(.wire-btn, .wire-action) {
.wrn-next--button-group[data-attached="true"] > :is(.wrn-btn, .wrn-action) {
position: relative;
margin: 0;
}
.wire-next--button-group[data-attached="true"] > .wire-action {
.wrn-next--button-group[data-attached="true"] > .wrn-action {
display: flex;
}
.wire-next--button-group[data-attached="true"] > :is(.wire-btn, .wire-action) :is(.wire-btn) {
.wrn-next--button-group[data-attached="true"] > :is(.wrn-btn, .wrn-action) :is(.wrn-btn) {
height: 100%;
}
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> :is(.wire-btn, .wire-action):not(:first-child) {
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> :is(.wrn-btn, .wrn-action):not(:first-child) {
margin-inline-start: -1px;
}
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-btn:not(:first-child):not(:last-child),
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-action:not(:first-child):not(:last-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-btn:not(:first-child):not(:last-child),
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-action:not(:first-child):not(:last-child)
> .wrn-btn {
border-radius: 0;
}
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-btn:first-child:not(:last-child),
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-action:first-child:not(:last-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-btn:first-child:not(:last-child),
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-action:first-child:not(:last-child)
> .wrn-btn {
border-start-end-radius: 0;
border-end-end-radius: 0;
}
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-btn:last-child:not(:first-child),
.wire-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wire-action:last-child:not(:first-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-btn:last-child:not(:first-child),
.wrn-next--button-group[data-attached="true"][data-orientation="horizontal"]
> .wrn-action:last-child:not(:first-child)
> .wrn-btn {
border-start-start-radius: 0;
border-end-start-radius: 0;
}
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> :is(.wire-btn, .wire-action):not(:first-child) {
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> :is(.wrn-btn, .wrn-action):not(:first-child) {
margin-top: -1px;
}
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-btn:not(:first-child):not(:last-child),
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-action:not(:first-child):not(:last-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-btn:not(:first-child):not(:last-child),
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-action:not(:first-child):not(:last-child)
> .wrn-btn {
border-radius: 0;
}
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-btn:first-child:not(:last-child),
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-action:first-child:not(:last-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-btn:first-child:not(:last-child),
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-action:first-child:not(:last-child)
> .wrn-btn {
border-end-start-radius: 0;
border-end-end-radius: 0;
}
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-btn:last-child:not(:first-child),
.wire-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wire-action:last-child:not(:first-child)
> .wire-btn {
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-btn:last-child:not(:first-child),
.wrn-next--button-group[data-attached="true"][data-orientation="vertical"]
> .wrn-action:last-child:not(:first-child)
> .wrn-btn {
border-start-start-radius: 0;
border-start-end-radius: 0;
}
.wire-next--button-group > :is(.wire-btn, .wire-action):focus-within,
.wire-next--button-group > .wire-btn:focus-visible {
.wrn-next--button-group > :is(.wrn-btn, .wrn-action):focus-within,
.wrn-next--button-group > .wrn-btn:focus-visible {
z-index: 2;
}
.wire-next--button-group[data-selectable="true"] > .wire-btn[data-selected="true"] {
.wrn-next--button-group[data-selectable="true"] > .wrn-btn[data-selected="true"] {
z-index: 1;
border-color: var(--wire-component-color);
background: color-mix(in srgb, var(--wire-component-color) 16%, var(--wire-color-surface));
color: var(--wire-component-color);
border-color: var(--wrn-component-color);
background: color-mix(in srgb, var(--wrn-component-color) 16%, var(--wrn-color-surface));
color: var(--wrn-component-color);
}
@media (max-width: 480px) {
.wire-next--button-group[data-responsive="true"] > :is(.wire-btn, .wire-action) {
.wrn-next--button-group[data-responsive="true"] > :is(.wrn-btn, .wrn-action) {
display: flex;
width: 100%;
flex-direction: column;
}
.wire-next--button-group[data-responsive="true"] > .wire-action > .wire-btn {
.wrn-next--button-group[data-responsive="true"] > .wrn-action > .wrn-btn {
width: 100%;
}
.wire-next--button-group[data-responsive="true"][data-attached="true"]
> :is(.wire-btn, .wire-action) {
.wrn-next--button-group[data-responsive="true"][data-attached="true"]
> :is(.wrn-btn, .wrn-action) {
margin-top: -1px;
margin-inline-start: 0;
}
.wire-next--button-group[data-responsive="true"][data-attached="true"]
> :is(.wire-btn, .wire-action):first-child {
.wrn-next--button-group[data-responsive="true"][data-attached="true"]
> :is(.wrn-btn, .wrn-action):first-child {
margin-top: 0;
}
.wire-next--button-group[data-responsive="true"][data-attached="true"] > .wire-btn,
.wire-next--button-group[data-responsive="true"][data-attached="true"]
> .wire-action
> .wire-btn {
.wrn-next--button-group[data-responsive="true"][data-attached="true"] > .wrn-btn,
.wrn-next--button-group[data-responsive="true"][data-attached="true"]
> .wrn-action
> .wrn-btn {
border-radius: 0;
}
.wire-next--button-group[data-responsive="true"][data-attached="true"] > .wire-btn:first-child,
.wire-next--button-group[data-responsive="true"][data-attached="true"]
> .wire-action:first-child
> .wire-btn {
border-start-start-radius: var(--wire-radius-sm);
border-start-end-radius: var(--wire-radius-sm);
.wrn-next--button-group[data-responsive="true"][data-attached="true"] > .wrn-btn:first-child,
.wrn-next--button-group[data-responsive="true"][data-attached="true"]
> .wrn-action:first-child
> .wrn-btn {
border-start-start-radius: var(--wrn-radius-sm);
border-start-end-radius: var(--wrn-radius-sm);
}
.wire-next--button-group[data-responsive="true"][data-attached="true"] > .wire-btn:last-child,
.wire-next--button-group[data-responsive="true"][data-attached="true"]
> .wire-action:last-child
> .wire-btn {
border-end-start-radius: var(--wire-radius-sm);
border-end-end-radius: var(--wire-radius-sm);
.wrn-next--button-group[data-responsive="true"][data-attached="true"] > .wrn-btn:last-child,
.wrn-next--button-group[data-responsive="true"][data-attached="true"]
> .wrn-action:last-child
> .wrn-btn {
border-end-start-radius: var(--wrn-radius-sm);
border-end-end-radius: var(--wrn-radius-sm);
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+194 -128
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
// Interactive, hydration-safe content carousel.
component Carousel {
outputs {
@@ -81,7 +79,7 @@ component Carousel {
if (!sourceEvent || !sourceEvent.currentTarget) {
return
}
root = sourceEvent.currentTarget.closest(".wire-next--carousel")
root = sourceEvent.currentTarget.closest(".wrn-next--carousel")
if (root) {
carouselRoot = root
}
@@ -94,8 +92,8 @@ component Carousel {
return
}
if (isSnap) {
viewport = root.querySelector(".wire-next__carousel-viewport")
slides = root.querySelectorAll(".wire-next__carousel-slide")
viewport = root.querySelector(".wrn-next__carousel-viewport")
slides = root.querySelectorAll(".wrn-next__carousel-slide")
slide = slides[index]
if (viewport && slide) {
viewport.scrollTo({
@@ -104,7 +102,7 @@ component Carousel {
})
}
}
thumbnails = root.querySelectorAll(".wire-next__carousel-thumbnails button")
thumbnails = root.querySelectorAll(".wrn-next__carousel-thumbnails button")
if (thumbnails[index]) {
thumbnail = thumbnails[index]
rail = thumbnail.parentElement
@@ -122,7 +120,7 @@ component Carousel {
}
rememberRoot(sourceEvent)
viewport = sourceEvent.currentTarget
slides = viewport.querySelectorAll(".wire-next__carousel-slide")
slides = viewport.querySelectorAll(".wrn-next__carousel-slide")
viewportCenter = viewport.scrollLeft + viewport.clientWidth / 2
closestIndex = currentIndex
closestDistance = 1000000000
@@ -144,7 +142,7 @@ component Carousel {
item: items[currentIndex],
reason: "snap"
})
slides = carouselRoot.querySelectorAll(".wire-next__carousel-thumbnails button")
slides = carouselRoot.querySelectorAll(".wrn-next__carousel-thumbnails button")
slide = slides[currentIndex]
if (slide) {
viewport = slide.parentElement
@@ -318,7 +316,7 @@ component Carousel {
view {
<section
{...attrs}
class="wire-component wire-next--carousel wire-component--color-{color} wire-component--size-{size} wire-next--variant-{variant} {class}"
class="wrn-component wrn-next--carousel wrn-component--color-{color} wrn-component--size-{size} wrn-next--variant-{variant} {class}"
data-rtl="{isRTL}"
data-centered="{isCentered}"
data-draggable="{isDraggable && !isSnap}"
@@ -337,15 +335,15 @@ component Carousel {
@focusout="startAutoplay()"
>
{#if title || description}
<header class="wire-next__carousel-header">
<header class="wrn-next__carousel-header">
{#if title}<h3>{title}</h3>{/if}
{#if description}<p>{description}</p>{/if}
</header>
{/if}
<div class="wire-next__carousel-layout">
<div class="wrn-next__carousel-layout">
{#if thumbnails === "vertical"}
<div class="wire-next__carousel-thumbnails" aria-label="Choose a slide">
<div class="wrn-next__carousel-thumbnails" aria-label="Choose a slide">
{#each items as item, index}
<button
type="button"
@@ -361,10 +359,10 @@ component Carousel {
</div>
{/if}
<div class="wire-next__carousel-main">
<div class="wire-next__carousel-stage">
<div class="wrn-next__carousel-main">
<div class="wrn-next__carousel-stage">
<div
class="wire-next__carousel-viewport"
class="wrn-next__carousel-viewport"
tabindex="0"
@scroll="handleSnapScroll(event)"
@pointerdown="beginDrag(event)"
@@ -374,12 +372,12 @@ component Carousel {
@lostpointercapture="cancelDrag()"
>
<div
class="wire-next__carousel-track"
style="--wire-carousel-index: {currentIndex}; --wire-carousel-per-view: {slidesPerView}; --wire-carousel-gap: {gap}; --wire-carousel-drag-offset: {dragOffset}px"
class="wrn-next__carousel-track"
style="--wrn-carousel-index: {currentIndex}; --wrn-carousel-per-view: {slidesPerView}; --wrn-carousel-gap: {gap}; --wrn-carousel-drag-offset: {dragOffset}px"
>
{#each items as item, index}
<article
class="wire-next__carousel-slide"
class="wrn-next__carousel-slide"
data-active="{index === currentIndex}"
role="group"
aria-roledescription="slide"
@@ -389,7 +387,7 @@ component Carousel {
{#if item.imageSrc}
<img src="{item.imageSrc}" alt="{item.imageAlt || item.title || ''}" />
{/if}
<div class="wire-next__carousel-slide-content">
<div class="wrn-next__carousel-slide-content">
{#if item.eyebrow}<span>{item.eyebrow}</span>{/if}
{#if item.title || item.label}<h4>{item.title || item.label}</h4>{/if}
{#if item.description}<p>{item.description}</p>{/if}
@@ -404,7 +402,7 @@ component Carousel {
</div>
<button
class="wire-next__carousel-control wire-next__carousel-control--previous"
class="wrn-next__carousel-control wrn-next__carousel-control--previous"
type="button"
aria-label="Previous slide"
disabled="{!isInfiniteLoop && currentIndex === 0}"
@@ -413,7 +411,7 @@ component Carousel {
<span class="icon-[lucide--chevron-left]" aria-hidden="true"></span>
</button>
<button
class="wire-next__carousel-control wire-next__carousel-control--next"
class="wrn-next__carousel-control wrn-next__carousel-control--next"
type="button"
aria-label="Next slide"
disabled="{!isInfiniteLoop && currentIndex === maximumIndex()}"
@@ -423,14 +421,14 @@ component Carousel {
</button>
{#if showCounter}
<output class="wire-next__carousel-counter" aria-live="polite">
<output class="wrn-next__carousel-counter" aria-live="polite">
{currentIndex + 1} / {items.length}
</output>
{/if}
</div>
{#if showPagination}
<div class="wire-next__carousel-pagination" aria-label="Choose a slide">
<div class="wrn-next__carousel-pagination" aria-label="Choose a slide">
{#each items as item, index}
<button
type="button"
@@ -444,7 +442,7 @@ component Carousel {
{/if}
{#if thumbnails === "horizontal"}
<div class="wire-next__carousel-thumbnails" aria-label="Choose a slide">
<div class="wrn-next__carousel-thumbnails" aria-label="Choose a slide">
{#each items as item, index}
<button
type="button"
@@ -462,145 +460,144 @@ component Carousel {
</div>
</div>
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--carousel {
--wire-carousel-color: var(--wire-component-color, var(--wire-color-primary));
.wrn-next--carousel {
--wrn-carousel-color: var(--wrn-component-color, var(--wrn-color-primary));
display: grid;
width: 100%;
min-width: 0;
gap: 1rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
color: var(--wire-color-text);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
}
.wire-next__carousel-header h3,
.wire-next__carousel-header p {
.wrn-next__carousel-header h3,
.wrn-next__carousel-header p {
margin: 0;
}
.wire-next__carousel-header p {
.wrn-next__carousel-header p {
margin-top: 0.35rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-next__carousel-layout {
.wrn-next__carousel-layout {
display: flex;
min-width: 0;
gap: 0.75rem;
}
.wire-next__carousel-main {
.wrn-next__carousel-main {
display: grid;
flex: 1;
min-width: 0;
gap: 0.75rem;
}
.wire-next__carousel-stage {
.wrn-next__carousel-stage {
position: relative;
min-width: 0;
}
.wire-next__carousel-viewport {
.wrn-next__carousel-viewport {
position: relative;
overflow: hidden;
min-width: 0;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface-subtle, var(--wire-color-surface));
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface-subtle, var(--wrn-color-surface));
outline: none;
}
.wire-next__carousel-viewport:focus-visible {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-carousel-color) 30%, transparent);
.wrn-next__carousel-viewport:focus-visible {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-carousel-color) 30%, transparent);
}
.wire-next__carousel-track {
.wrn-next__carousel-track {
display: flex;
align-items: stretch;
gap: var(--wire-carousel-gap);
gap: var(--wrn-carousel-gap);
transform: translateX(
calc(
var(--wire-carousel-index) * -1 *
var(--wrn-carousel-index) * -1 *
(
(100% - (var(--wire-carousel-per-view) - 1) * var(--wire-carousel-gap)) /
var(--wire-carousel-per-view) + var(--wire-carousel-gap)
(100% - (var(--wrn-carousel-per-view) - 1) * var(--wrn-carousel-gap)) /
var(--wrn-carousel-per-view) + var(--wrn-carousel-gap)
)
)
);
transition: transform var(--wire-motion-base) var(--wire-ease-emphasized);
translate: var(--wire-carousel-drag-offset, 0) 0;
transition: transform var(--wrn-motion-base) var(--wrn-ease-emphasized);
translate: var(--wrn-carousel-drag-offset, 0) 0;
will-change: transform;
}
.wire-next--carousel[data-dragging="true"] .wire-next__carousel-track {
.wrn-next--carousel[data-dragging="true"] .wrn-next__carousel-track {
transition: none;
}
.wire-next--carousel[data-rtl="true"] .wire-next__carousel-track {
.wrn-next--carousel[data-rtl="true"] .wrn-next__carousel-track {
transform: translateX(
calc(
var(--wire-carousel-index) *
var(--wrn-carousel-index) *
(
(100% - (var(--wire-carousel-per-view) - 1) * var(--wire-carousel-gap)) /
var(--wire-carousel-per-view) + var(--wire-carousel-gap)
(100% - (var(--wrn-carousel-per-view) - 1) * var(--wrn-carousel-gap)) /
var(--wrn-carousel-per-view) + var(--wrn-carousel-gap)
)
)
);
}
.wire-next__carousel-slide {
.wrn-next__carousel-slide {
position: relative;
display: grid;
flex: 0 0
calc(
(100% - (var(--wire-carousel-per-view) - 1) * var(--wire-carousel-gap)) /
var(--wire-carousel-per-view)
(100% - (var(--wrn-carousel-per-view) - 1) * var(--wrn-carousel-gap)) /
var(--wrn-carousel-per-view)
);
overflow: hidden;
min-width: 0;
min-height: 20rem;
place-items: center;
background: var(--wire-color-surface-raised, var(--wire-color-surface));
background: var(--wrn-color-surface-raised, var(--wrn-color-surface));
}
.wire-next--carousel[data-auto-height="true"] .wire-next__carousel-slide {
.wrn-next--carousel[data-auto-height="true"] .wrn-next__carousel-slide {
min-height: 0;
}
.wire-next--carousel[data-centered="true"] .wire-next__carousel-slide {
.wrn-next--carousel[data-centered="true"] .wrn-next__carousel-slide {
flex-basis: min(78%, 32rem);
}
.wire-next--carousel[data-centered="true"] .wire-next__carousel-track {
.wrn-next--carousel[data-centered="true"] .wrn-next__carousel-track {
box-sizing: border-box;
transform: translateX(
calc(var(--wire-carousel-index) * -1 * (min(78%, 32rem) + var(--wire-carousel-gap)))
calc(var(--wrn-carousel-index) * -1 * (min(78%, 32rem) + var(--wrn-carousel-gap)))
);
}
.wire-next--carousel[data-centered="true"] .wire-next__carousel-slide:first-child {
.wrn-next--carousel[data-centered="true"] .wrn-next__carousel-slide:first-child {
margin-inline-start: calc((100% - min(78%, 32rem)) / 2);
}
.wire-next--carousel[data-centered="true"] .wire-next__carousel-slide:last-child {
.wrn-next--carousel[data-centered="true"] .wrn-next__carousel-slide:last-child {
margin-inline-end: calc((100% - min(78%, 32rem)) / 2);
}
.wire-next--carousel[data-centered="true"][data-rtl="true"] .wire-next__carousel-track {
.wrn-next--carousel[data-centered="true"][data-rtl="true"] .wrn-next__carousel-track {
transform: translateX(
calc(var(--wire-carousel-index) * (min(78%, 32rem) + var(--wire-carousel-gap)))
calc(var(--wrn-carousel-index) * (min(78%, 32rem) + var(--wrn-carousel-gap)))
);
}
.wire-next__carousel-slide > img {
.wrn-next__carousel-slide > img {
position: absolute;
width: 100%;
height: 100%;
@@ -608,7 +605,7 @@ component Carousel {
inset: 0;
}
.wire-next__carousel-slide-content {
.wrn-next__carousel-slide-content {
position: relative;
z-index: 1;
width: min(100%, 38rem);
@@ -616,21 +613,21 @@ component Carousel {
text-align: center;
}
.wire-next__carousel-slide-content h4,
.wire-next__carousel-slide-content p {
.wrn-next__carousel-slide-content h4,
.wrn-next__carousel-slide-content p {
margin: 0;
}
.wire-next__carousel-slide-content h4 {
.wrn-next__carousel-slide-content h4 {
font-size: clamp(1.5rem, 4vw, 2.4rem);
}
.wire-next__carousel-slide-content p {
.wrn-next__carousel-slide-content p {
margin-top: 0.65rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-next__carousel-control {
.wrn-next__carousel-control {
position: absolute;
z-index: 3;
top: 50%;
@@ -638,46 +635,46 @@ component Carousel {
width: 2.75rem;
min-height: 2.75rem;
padding: 0;
border: 1px solid color-mix(in srgb, var(--wire-color-border) 70%, transparent);
border: 1px solid color-mix(in srgb, var(--wrn-color-border) 70%, transparent);
border-radius: 999px;
background: color-mix(in srgb, var(--wire-color-text) 76%, transparent);
color: var(--wire-color-surface);
background: color-mix(in srgb, var(--wrn-color-text) 76%, transparent);
color: var(--wrn-color-surface);
cursor: pointer;
place-items: center;
transform: translateY(-50%);
}
.wire-next__carousel-control:focus-visible,
.wire-next__carousel-pagination button:focus-visible,
.wire-next__carousel-thumbnails button:focus-visible {
outline: 2px solid var(--wire-carousel-color);
.wrn-next__carousel-control:focus-visible,
.wrn-next__carousel-pagination button:focus-visible,
.wrn-next__carousel-thumbnails button:focus-visible {
outline: 2px solid var(--wrn-carousel-color);
outline-offset: 2px;
}
.wire-next__carousel-control:disabled {
.wrn-next__carousel-control:disabled {
cursor: not-allowed;
opacity: 0.35;
}
.wire-next__carousel-control--previous {
.wrn-next__carousel-control--previous {
left: 1rem;
}
.wire-next__carousel-control--next {
.wrn-next__carousel-control--next {
right: 1rem;
}
.wire-next--carousel[data-rtl="true"] .wire-next__carousel-control--previous {
.wrn-next--carousel[data-rtl="true"] .wrn-next__carousel-control--previous {
right: 1rem;
left: auto;
}
.wire-next--carousel[data-rtl="true"] .wire-next__carousel-control--next {
.wrn-next--carousel[data-rtl="true"] .wrn-next__carousel-control--next {
right: auto;
left: 1rem;
}
.wire-next__carousel-pagination {
.wrn-next__carousel-pagination {
display: flex;
min-height: 1.5rem;
align-items: center;
@@ -685,148 +682,217 @@ component Carousel {
gap: 0.5rem;
}
.wire-next__carousel-pagination button {
.wrn-next__carousel-pagination button {
width: 0.65rem;
min-height: 0.65rem;
padding: 0;
border: 1px solid var(--wire-color-text-muted);
border: 1px solid var(--wrn-color-text-muted);
border-radius: 999px;
background: transparent;
cursor: pointer;
}
.wire-next__carousel-pagination button[data-active="true"] {
border-color: var(--wire-carousel-color);
background: var(--wire-carousel-color);
.wrn-next__carousel-pagination button[data-active="true"] {
border-color: var(--wrn-carousel-color);
background: var(--wrn-carousel-color);
}
.wire-next__carousel-counter {
.wrn-next__carousel-counter {
position: absolute;
z-index: 3;
bottom: 0.8rem;
left: 50%;
padding: 0.25rem 0.65rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wire-color-text) 82%, transparent);
color: var(--wire-color-surface);
background: color-mix(in srgb, var(--wrn-color-text) 82%, transparent);
color: var(--wrn-color-surface);
font-weight: 700;
transform: translateX(-50%);
}
.wire-next__carousel-thumbnails {
.wrn-next__carousel-thumbnails {
display: flex;
overflow: auto;
gap: 0.55rem;
scrollbar-width: thin;
}
.wire-next__carousel-thumbnails button {
.wrn-next__carousel-thumbnails button {
display: grid;
flex: 0 0 8.5rem;
min-height: 3.75rem;
overflow: hidden;
padding: 0;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
cursor: pointer;
place-items: center;
}
.wire-next__carousel-thumbnails button[data-active="true"] {
border-color: var(--wire-carousel-color);
box-shadow: inset 0 0 0 1px var(--wire-carousel-color);
.wrn-next__carousel-thumbnails button[data-active="true"] {
border-color: var(--wrn-carousel-color);
box-shadow: inset 0 0 0 1px var(--wrn-carousel-color);
}
.wire-next__carousel-thumbnails img {
.wrn-next__carousel-thumbnails img {
width: 100%;
height: 4rem;
object-fit: cover;
}
.wire-next__carousel-thumbnails span {
.wrn-next__carousel-thumbnails span {
padding: 0.5rem;
}
.wire-next--carousel[data-thumbnails="vertical"] .wire-next__carousel-thumbnails {
.wrn-next--carousel[data-thumbnails="vertical"] .wrn-next__carousel-thumbnails {
flex: 0 0 9rem;
flex-direction: column;
max-height: 28rem;
}
.wire-next--carousel[data-thumbnails="vertical"] .wire-next__carousel-thumbnails button {
.wrn-next--carousel[data-thumbnails="vertical"] .wrn-next__carousel-thumbnails button {
flex-basis: 7.5rem;
width: 100%;
}
.wire-next--carousel[data-draggable="true"] .wire-next__carousel-viewport {
.wrn-next--carousel[data-draggable="true"] .wrn-next__carousel-viewport {
cursor: grab;
touch-action: pan-y;
user-select: none;
-webkit-user-select: none;
}
.wire-next--carousel[data-draggable="true"] .wire-next__carousel-viewport:active {
.wrn-next--carousel[data-draggable="true"] .wrn-next__carousel-viewport:active {
cursor: grabbing;
}
.wire-next--carousel[data-draggable="true"] img,
.wire-next--carousel[data-draggable="true"] a {
.wrn-next--carousel[data-draggable="true"] img,
.wrn-next--carousel[data-draggable="true"] a {
-webkit-user-drag: none;
}
.wire-next--carousel[data-snap="true"] .wire-next__carousel-viewport {
.wrn-next--carousel[data-snap="true"] .wrn-next__carousel-viewport {
overflow-x: auto;
scroll-behavior: smooth;
scroll-snap-type: x mandatory;
scrollbar-width: thin;
}
.wire-next--carousel[data-snap="true"] .wire-next__carousel-track {
--wire-carousel-snap-slide: calc(
(100% - (var(--wire-carousel-per-view) - 1) * var(--wire-carousel-gap)) /
var(--wire-carousel-per-view)
.wrn-next--carousel[data-snap="true"] .wrn-next__carousel-track {
--wrn-carousel-snap-slide: calc(
(100% - (var(--wrn-carousel-per-view) - 1) * var(--wrn-carousel-gap)) /
var(--wrn-carousel-per-view)
);
display: grid;
width: 100%;
min-width: 100%;
box-sizing: content-box;
grid-auto-columns: var(--wire-carousel-snap-slide);
grid-auto-columns: var(--wrn-carousel-snap-slide);
grid-auto-flow: column;
padding-inline: calc((100% - var(--wire-carousel-snap-slide)) / 2);
padding-inline: calc((100% - var(--wrn-carousel-snap-slide)) / 2);
transform: none;
}
.wire-next--carousel[data-snap="true"] .wire-next__carousel-slide {
.wrn-next--carousel[data-snap="true"] .wrn-next__carousel-slide {
width: auto;
min-width: 0;
scroll-snap-align: center;
}
@media (max-width: 640px) {
.wire-next__carousel-slide {
.wrn-next__carousel-slide {
flex-basis: 100%;
min-height: 16rem;
}
.wire-next--carousel[data-thumbnails="vertical"] .wire-next__carousel-layout {
.wrn-next--carousel[data-thumbnails="vertical"] .wrn-next__carousel-layout {
flex-direction: column-reverse;
}
.wire-next--carousel[data-thumbnails="vertical"] .wire-next__carousel-thumbnails {
.wrn-next--carousel[data-thumbnails="vertical"] .wrn-next__carousel-thumbnails {
flex: auto;
flex-direction: row;
max-height: none;
}
.wire-next--carousel[data-thumbnails="vertical"] .wire-next__carousel-thumbnails button {
.wrn-next--carousel[data-thumbnails="vertical"] .wrn-next__carousel-thumbnails button {
flex-basis: 8.5rem;
width: auto;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-next__carousel-track {
.wrn-next__carousel-track {
transition-duration: 0.01ms;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+23 -23
View File
@@ -51,31 +51,31 @@ component Chart {
}
view {
<figure {...attrs} data-ui-component="Chart" data-variant='{variant}' class='wire-chart {class}'>
<figure {...attrs} data-ui-component="Chart" data-variant='{variant}' class='wrn-chart {class}'>
{#if title || description}
<figcaption class="wire-chart__header">
<figcaption class="wrn-chart__header">
{#if title}<strong>{title}</strong>{/if}
{#if description}<span>{description}</span>{/if}
</figcaption>
{/if}
<svg class="wire-chart__plot" viewBox="0 0 100 100" role="img" aria-label='{title}' style='height:{height}px'>
<line x1="8" y1="84" x2="96" y2="84" class="wire-chart__axis" />
<svg class="wrn-chart__plot" viewBox="0 0 100 100" role="img" aria-label='{title}' style='height:{height}px'>
<line x1="8" y1="84" x2="96" y2="84" class="wrn-chart__axis" />
{#each items as item, index}
<g data-hidden='{isHidden(index) ? "true" : "false"}'>
<rect
x='{barX(index)}' y='{barY(item)}' width='{barWidth()}' height='{barHeight(item)}'
rx="1.5" class="wire-chart__bar" role="button" tabindex="0"
rx="1.5" class="wrn-chart__bar" role="button" tabindex="0"
aria-label='{item[labelKey] + ": " + numericValue(item)}'
@click='selectPoint(item, index, event)'
@keydown='if (event.key === "Enter" || event.key === " ") { event.preventDefault(); selectPoint(item, index, event) }'
/>
{#if showValues}<text x='{barX(index) + barWidth() / 2}' y='{barY(item) - 3}' class="wire-chart__value">{numericValue(item)}</text>{/if}
<text x='{barX(index) + barWidth() / 2}' y="94" class="wire-chart__label">{item[labelKey]}</text>
{#if showValues}<text x='{barX(index) + barWidth() / 2}' y='{barY(item) - 3}' class="wrn-chart__value">{numericValue(item)}</text>{/if}
<text x='{barX(index) + barWidth() / 2}' y="94" class="wrn-chart__label">{item[labelKey]}</text>
</g>
{/each}
</svg>
{#if showLegend}
<div class="wire-chart__legend" aria-label="Chart legend">
<div class="wrn-chart__legend" aria-label="Chart legend">
{#each items as item, index}
<button type="button" aria-pressed='{isHidden(index) ? "false" : "true"}' @click='toggleLegend(item, index, event)'>
<span aria-hidden="true"></span>{item[labelKey]}
@@ -88,20 +88,20 @@ component Chart {
}
style {
.wire-chart { display: grid; gap: 1rem; min-width: 0; margin: 0; padding: 1rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); color: var(--wire-color-text); background: var(--wire-color-surface); box-shadow: var(--wire-shadow-1); }
.wire-chart__header { display: grid; gap: 0.2rem; }
.wire-chart__header > span { color: var(--wire-color-muted); font-size: 0.8rem; }
.wire-chart__plot { display: block; width: 100%; min-height: 12rem; overflow: visible; }
.wire-chart__axis { stroke: var(--wire-color-border); stroke-width: 0.5; }
.wire-chart__bar { fill: var(--wire-color-primary); cursor: pointer; transition: opacity var(--wire-motion-fast), transform var(--wire-motion-fast); transform-box: fill-box; transform-origin: bottom; }
.wire-chart__bar:hover { opacity: 0.82; transform: scaleY(1.02); }
.wire-chart__bar:focus-visible { outline: 1.5px solid var(--wire-color-focus); outline-offset: 1px; }
.wire-chart__plot g[data-hidden="true"] { opacity: 0.18; pointer-events: none; }
.wire-chart__value, .wire-chart__label { fill: var(--wire-color-text); font-size: 3.5px; text-anchor: middle; }
.wire-chart__label { fill: var(--wire-color-muted); }
.wire-chart__legend { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-chart__legend button { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.5rem; border: 1px solid var(--wire-color-border); border-radius: 999px; color: var(--wire-color-text); background: transparent; font: inherit; font-size: 0.75rem; cursor: pointer; }
.wire-chart__legend button > span { width: 0.65rem; height: 0.65rem; border-radius: 0.2rem; background: var(--wire-color-primary); }
.wire-chart__legend button[aria-pressed="false"] { opacity: 0.5; }
.wrn-chart { display: grid; gap: 1rem; min-width: 0; margin: 0; padding: 1rem; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius); color: var(--wrn-color-text); background: var(--wrn-color-surface); box-shadow: var(--wrn-shadow-1); }
.wrn-chart__header { display: grid; gap: 0.2rem; }
.wrn-chart__header > span { color: var(--wrn-color-muted); font-size: 0.8rem; }
.wrn-chart__plot { display: block; width: 100%; min-height: 12rem; overflow: visible; }
.wrn-chart__axis { stroke: var(--wrn-color-border); stroke-width: 0.5; }
.wrn-chart__bar { fill: var(--wrn-color-primary); cursor: pointer; transition: opacity var(--wrn-motion-fast), transform var(--wrn-motion-fast); transform-box: fill-box; transform-origin: bottom; }
.wrn-chart__bar:hover { opacity: 0.82; transform: scaleY(1.02); }
.wrn-chart__bar:focus-visible { outline: 1.5px solid var(--wrn-color-focus); outline-offset: 1px; }
.wrn-chart__plot g[data-hidden="true"] { opacity: 0.18; pointer-events: none; }
.wrn-chart__value, .wrn-chart__label { fill: var(--wrn-color-text); font-size: 3.5px; text-anchor: middle; }
.wrn-chart__label { fill: var(--wrn-color-muted); }
.wrn-chart__legend { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-chart__legend button { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.5rem; border: 1px solid var(--wrn-color-border); border-radius: 999px; color: var(--wrn-color-text); background: transparent; font: inherit; font-size: 0.75rem; cursor: pointer; }
.wrn-chart__legend button > span { width: 0.65rem; height: 0.65rem; border-radius: 0.2rem; background: var(--wrn-color-primary); }
.wrn-chart__legend button[aria-pressed="false"] { opacity: 0.5; }
}
}
+136 -70
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ChatBubble {
outputs {
action(payload: { action: boolean; item: string | number | boolean | null | object; index: number })
@@ -66,31 +64,31 @@ component ChatBubble {
view {
<section
{...attrs}
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--chat-bubble wire-next--variant-{variant} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--chat-bubble wrn-next--variant-{variant} {class}"
data-one-sided="{oneSided}"
role="log"
aria-label="{ariaLabel}"
aria-live="polite"
>
{#if title || description}
<header class="wire-next__chat-header">
<header class="wrn-next__chat-header">
{#if title}<h3>{title}</h3>{/if}
{#if description}<p>{description}</p>{/if}
</header>
{/if}
{#if items.length}
<div class="wire-next__chat-thread">
<div class="wrn-next__chat-thread">
{#each items as item, index}
<article
class="wire-next__chat-message"
class="wrn-next__chat-message"
data-direction="{messageDirection(item)}"
aria-label="{messageDirection(item) === 'outgoing' ? 'Sent message' : 'Received message'}"
>
<div class="wire-next__chat-row">
<div class="wrn-next__chat-row">
{#if showAvatars && (item.avatarSrc || item.avatarFallback)}
<button
class="wire-next__chat-avatar"
class="wrn-next__chat-avatar"
type="button"
aria-label="{item.avatarLabel || item.author || 'Message author'}"
@click="selectAvatar(event, item, index)"
@@ -104,7 +102,7 @@ component ChatBubble {
{/if}
<div
class="wire-next__chat-content"
class="wrn-next__chat-content"
role="button"
tabindex="0"
@click="selectMessage(item, index)"
@@ -131,7 +129,7 @@ component ChatBubble {
</div>
{#if showMetadata && (item.timestamp || item.status || item.actionLabel)}
<footer class="wire-next__chat-meta" data-tone="{item.statusTone || 'muted'}">
<footer class="wrn-next__chat-meta" data-tone="{item.statusTone || 'muted'}">
{#if item.statusIcon}<span class="{item.statusIcon}" aria-hidden="true"></span>{/if}
{#if item.status}<span>{item.status}</span>{/if}
{#if item.timestamp}<time datetime="{item.datetime || ''}">{item.timestamp}</time>{/if}
@@ -148,48 +146,47 @@ component ChatBubble {
{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--chat-bubble.wire-component--size-sm {
--wire-chat-padding: 0.75rem;
.wrn-next--chat-bubble.wrn-component--size-sm {
--wrn-chat-padding: 0.75rem;
font-size: 0.875rem;
}
.wire-next--chat-bubble.wire-component--size-lg {
--wire-chat-padding: 1.25rem;
.wrn-next--chat-bubble.wrn-component--size-lg {
--wrn-chat-padding: 1.25rem;
font-size: 1.0625rem;
}
.wire-next--chat-bubble {
--wire-chat-color: var(--wire-component-color, var(--wire-color-primary));
--wire-chat-padding: 1rem;
.wrn-next--chat-bubble {
--wrn-chat-color: var(--wrn-component-color, var(--wrn-color-primary));
--wrn-chat-padding: 1rem;
display: grid;
width: 100%;
min-width: 0;
gap: 1rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-next__chat-header h3,
.wire-next__chat-header p,
.wire-next__chat-content h4,
.wire-next__chat-content p {
.wrn-next__chat-header h3,
.wrn-next__chat-header p,
.wrn-next__chat-content h4,
.wrn-next__chat-content p {
margin: 0;
}
.wire-next__chat-header p {
.wrn-next__chat-header p {
margin-top: 0.35rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-next__chat-thread {
.wrn-next__chat-thread {
display: grid;
min-width: 0;
gap: 1.25rem;
}
.wire-next__chat-message {
.wrn-next__chat-message {
display: grid;
width: min(78%, 34rem);
min-width: 0;
@@ -197,134 +194,134 @@ component ChatBubble {
gap: 0.4rem;
}
.wire-next__chat-message[data-direction="outgoing"] {
.wrn-next__chat-message[data-direction="outgoing"] {
justify-self: end;
}
.wire-next--chat-bubble[data-one-sided="true"] .wire-next__chat-message {
.wrn-next--chat-bubble[data-one-sided="true"] .wrn-next__chat-message {
justify-self: start;
}
.wire-next__chat-row {
.wrn-next__chat-row {
display: flex;
min-width: 0;
align-items: flex-start;
gap: 0.75rem;
}
.wire-next__chat-message[data-direction="outgoing"] .wire-next__chat-row {
.wrn-next__chat-message[data-direction="outgoing"] .wrn-next__chat-row {
flex-direction: row-reverse;
}
.wire-next--chat-bubble[data-one-sided="true"]
.wire-next__chat-message[data-direction="outgoing"]
.wire-next__chat-row {
.wrn-next--chat-bubble[data-one-sided="true"]
.wrn-next__chat-message[data-direction="outgoing"]
.wrn-next__chat-row {
flex-direction: row;
}
.wire-next__chat-content {
.wrn-next__chat-content {
min-width: 0;
padding: var(--wire-chat-padding);
border: 1px solid var(--wire-color-border);
padding: var(--wrn-chat-padding);
border: 1px solid var(--wrn-color-border);
border-radius: 1rem;
background: var(--wire-color-surface-raised, var(--wire-color-surface));
color: var(--wire-color-text);
background: var(--wrn-color-surface-raised, var(--wrn-color-surface));
color: var(--wrn-color-text);
cursor: pointer;
}
.wire-next__chat-content:focus-visible {
outline: 2px solid var(--wire-chat-color);
.wrn-next__chat-content:focus-visible {
outline: 2px solid var(--wrn-chat-color);
outline-offset: 2px;
}
.wire-next__chat-message[data-direction="outgoing"] .wire-next__chat-content {
border-color: var(--wire-chat-color);
background: var(--wire-chat-color);
color: var(--wire-color-primary-contrast, #fff);
.wrn-next__chat-message[data-direction="outgoing"] .wrn-next__chat-content {
border-color: var(--wrn-chat-color);
background: var(--wrn-chat-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wire-next__chat-message[data-direction="outgoing"] .wire-next__chat-content :is(h4, p, li, span) {
.wrn-next__chat-message[data-direction="outgoing"] .wrn-next__chat-content :is(h4, p, li, span) {
color: inherit;
}
.wire-next__chat-content h4 {
.wrn-next__chat-content h4 {
margin-bottom: 0.65rem;
font-size: 1em;
}
.wire-next__chat-content p + p,
.wire-next__chat-content p + nav {
.wrn-next__chat-content p + p,
.wrn-next__chat-content p + nav {
margin-top: 0.75rem;
}
.wire-next__chat-content ul {
.wrn-next__chat-content ul {
display: grid;
margin: 0.55rem 0 0;
padding-inline-start: 1.25rem;
gap: 0.35rem;
}
.wire-next__chat-content nav {
.wrn-next__chat-content nav {
display: grid;
margin-top: 0.75rem;
gap: 0.3rem;
}
.wire-next__chat-content a {
color: var(--wire-chat-color);
.wrn-next__chat-content a {
color: var(--wrn-chat-color);
font-weight: 650;
text-decoration: none;
}
.wire-next__chat-message[data-direction="outgoing"] .wire-next__chat-content a {
.wrn-next__chat-message[data-direction="outgoing"] .wrn-next__chat-content a {
color: inherit;
text-decoration: underline;
}
.wire-next__chat-avatar {
.wrn-next__chat-avatar {
display: grid;
flex: 0 0 2.5rem;
width: 2.5rem;
min-height: 2.5rem;
overflow: hidden;
padding: 0;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 999px;
background: var(--wire-color-surface-raised, var(--wire-color-surface));
color: var(--wire-color-text);
background: var(--wrn-color-surface-raised, var(--wrn-color-surface));
color: var(--wrn-color-text);
cursor: pointer;
place-items: center;
}
.wire-next__chat-avatar img {
.wrn-next__chat-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.wire-next__chat-avatar:focus-visible {
outline: 2px solid var(--wire-chat-color);
.wrn-next__chat-avatar:focus-visible {
outline: 2px solid var(--wrn-chat-color);
outline-offset: 2px;
}
.wire-next__chat-meta {
.wrn-next__chat-meta {
display: flex;
align-items: center;
gap: 0.3rem;
padding-inline: calc(2.5rem + 0.75rem);
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.78em;
}
.wire-next__chat-message[data-direction="outgoing"] .wire-next__chat-meta {
.wrn-next__chat-message[data-direction="outgoing"] .wrn-next__chat-meta {
justify-content: flex-end;
}
.wire-next__chat-meta[data-tone="danger"] {
color: var(--wire-color-danger);
.wrn-next__chat-meta[data-tone="danger"] {
color: var(--wrn-color-danger);
}
.wire-next__chat-meta button {
.wrn-next__chat-meta button {
padding: 0;
border: 0;
background: transparent;
@@ -336,17 +333,86 @@ component ChatBubble {
}
@media (max-width: 640px) {
.wire-next__chat-message {
.wrn-next__chat-message {
width: min(92%, 34rem);
}
.wire-next__chat-avatar {
.wrn-next__chat-avatar {
flex-basis: 2rem;
width: 2rem;
min-height: 2rem;
}
.wire-next__chat-meta {
.wrn-next__chat-meta {
padding-inline: 0;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+21 -21
View File
@@ -54,25 +54,25 @@ component Clipboard {
data-status='{status}'
data-size='{size}'
data-color='{color}'
class='wire-clipboard {class}'
class='wrn-clipboard {class}'
>
{#if title}<strong class="wire-clipboard__title">{title}</strong>{/if}
{#if description}<p class="wire-clipboard__description">{description}</p>{/if}
<div class="wire-clipboard__control">
{#if title}<strong class="wrn-clipboard__title">{title}</strong>{/if}
{#if description}<p class="wrn-clipboard__description">{description}</p>{/if}
<div class="wrn-clipboard__control">
{#if code}
<code class="wire-clipboard__value">{value}</code>
<code class="wrn-clipboard__value">{value}</code>
{:else}
<span class="wire-clipboard__value">{value}</span>
<span class="wrn-clipboard__value">{value}</span>
{/if}
<button
type="button"
class="wire-clipboard__button"
class="wrn-clipboard__button"
disabled='{disabled}'
aria-label='{status === "success" ? copiedLabel : status === "error" ? errorLabel : label}'
@click='copyValue(event)'
>
<span class="icon-[lucide--copy] wire-clipboard__icon" data-show='status !== "success"' aria-hidden="true"></span>
<span class="icon-[lucide--check] wire-clipboard__icon" data-show='status === "success"' aria-hidden="true"></span>
<span class="icon-[lucide--copy] wrn-clipboard__icon" data-show='status !== "success"' aria-hidden="true"></span>
<span class="icon-[lucide--check] wrn-clipboard__icon" data-show='status === "success"' aria-hidden="true"></span>
<span>{status === "success" ? copiedLabel : status === "error" ? errorLabel : label}</span>
</button>
</div>
@@ -81,17 +81,17 @@ component Clipboard {
}
style {
.wire-clipboard { display: grid; gap: 0.5rem; color: var(--wire-color-text); }
.wire-clipboard__title { font-size: 0.875rem; }
.wire-clipboard__description { margin: 0; color: var(--wire-color-muted); font-size: 0.8rem; }
.wire-clipboard__control { display: flex; min-width: 0; align-items: stretch; overflow: hidden; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); background: var(--wire-color-surface); box-shadow: var(--wire-shadow-1); }
.wire-clipboard__value { min-width: 0; flex: 1; overflow: auto; padding: 0.7rem 0.85rem; color: var(--wire-color-text); background: var(--wire-color-surface-2); font: inherit; font-family: var(--wire-font-mono, ui-monospace, monospace); white-space: nowrap; }
.wire-clipboard__button { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.6rem 0.85rem; border: 0; border-left: 1px solid var(--wire-color-border); color: var(--wire-color-primary); background: transparent; font: inherit; font-size: 0.8rem; font-weight: 700; cursor: pointer; }
.wire-clipboard__button:hover { background: var(--wire-color-primary-soft); }
.wire-clipboard__button:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: -3px; }
.wire-clipboard__button:disabled { cursor: not-allowed; opacity: 0.55; }
.wire-clipboard[data-status="success"] .wire-clipboard__button { color: var(--wire-color-success); }
.wire-clipboard[data-status="error"] .wire-clipboard__button { color: var(--wire-color-danger); }
.wire-clipboard__icon { width: 1rem; height: 1rem; flex: none; }
.wrn-clipboard { display: grid; gap: 0.5rem; color: var(--wrn-color-text); }
.wrn-clipboard__title { font-size: 0.875rem; }
.wrn-clipboard__description { margin: 0; color: var(--wrn-color-muted); font-size: 0.8rem; }
.wrn-clipboard__control { display: flex; min-width: 0; align-items: stretch; overflow: hidden; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius); background: var(--wrn-color-surface); box-shadow: var(--wrn-shadow-1); }
.wrn-clipboard__value { min-width: 0; flex: 1; overflow: auto; padding: 0.7rem 0.85rem; color: var(--wrn-color-text); background: var(--wrn-color-surface-2); font: inherit; font-family: var(--wrn-font-mono, ui-monospace, monospace); white-space: nowrap; }
.wrn-clipboard__button { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.6rem 0.85rem; border: 0; border-left: 1px solid var(--wrn-color-border); color: var(--wrn-color-primary); background: transparent; font: inherit; font-size: 0.8rem; font-weight: 700; cursor: pointer; }
.wrn-clipboard__button:hover { background: var(--wrn-color-primary-soft); }
.wrn-clipboard__button:focus-visible { outline: 2px solid var(--wrn-color-focus); outline-offset: -3px; }
.wrn-clipboard__button:disabled { cursor: not-allowed; opacity: 0.55; }
.wrn-clipboard[data-status="success"] .wrn-clipboard__button { color: var(--wrn-color-success); }
.wrn-clipboard[data-status="error"] .wrn-clipboard__button { color: var(--wrn-color-danger); }
.wrn-clipboard__icon { width: 1rem; height: 1rem; flex: none; }
}
}
+117 -51
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Collapse {
outputs {
toggle(payload: { index: number; item: string | number | boolean | null | object; open: boolean; openIndexes: number[] })
@@ -53,18 +51,18 @@ size: string = "default"
view {
<section
{...attrs}
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--collapse {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--collapse {class}"
data-mode="{mode}"
aria-label="{ariaLabel}"
>
{#each items as item, index}
<article class="wire-next__collapse-item" data-open="{isOpen(index)}">
<article class="wrn-next__collapse-item" data-open="{isOpen(index)}">
{#if mode === "inline" && item.preview}
<p class="wire-next__collapse-preview">{item.preview}</p>
<p class="wrn-next__collapse-preview">{item.preview}</p>
{/if}
<button
class="wire-next__collapse-trigger"
class="wrn-next__collapse-trigger"
type="button"
aria-expanded="{isOpen(index) ? 'true' : 'false'}"
aria-controls="{item.id || 'collapse-panel-' + index}"
@@ -72,17 +70,17 @@ size: string = "default"
@click="toggleItem(index, item)"
>
<span>{isOpen(index) ? (item.closeLabel || "Read less") : item.label}</span>
<span class="icon-[lucide--chevron-down] wire-next__collapse-chevron" aria-hidden="true"></span>
<span class="icon-[lucide--chevron-down] wrn-next__collapse-chevron" aria-hidden="true"></span>
</button>
<div
id="{item.id || 'collapse-panel-' + index}"
class="wire-next__collapse-panel"
class="wrn-next__collapse-panel"
data-open="{isOpen(index)}"
aria-hidden="{isOpen(index) ? 'false' : 'true'}"
inert="{!isOpen(index)}"
>
<div class="wire-next__collapse-content">
<div class="wrn-next__collapse-content">
{#if item.title}<h4>{item.title}</h4>{/if}
{#if item.content}<p>{item.content}</p>{/if}
{#if item.links && item.links.length}
@@ -96,133 +94,132 @@ size: string = "default"
{/each}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--collapse.wire-component--size-sm {
.wrn-next--collapse.wrn-component--size-sm {
font-size: 0.875rem;
}
.wire-next--collapse.wire-component--size-lg {
.wrn-next--collapse.wrn-component--size-lg {
font-size: 1.0625rem;
}
.wire-next--collapse {
--wire-collapse-color: var(--wire-component-color, var(--wire-color-primary));
.wrn-next--collapse {
--wrn-collapse-color: var(--wrn-component-color, var(--wrn-color-primary));
display: grid;
width: 100%;
min-width: 0;
gap: 0.75rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-next__collapse-item {
.wrn-next__collapse-item {
display: grid;
min-width: 0;
justify-items: start;
gap: 0;
}
.wire-next__collapse-trigger {
.wrn-next__collapse-trigger {
display: inline-flex;
min-height: 2.75rem;
align-items: center;
justify-content: center;
gap: 0.55rem;
padding: 0.7rem 1rem;
border: 1px solid var(--wire-collapse-color);
border-radius: var(--wire-radius-sm);
background: var(--wire-collapse-color);
color: var(--wire-color-primary-contrast, #fff);
border: 1px solid var(--wrn-collapse-color);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-collapse-color);
color: var(--wrn-color-primary-contrast, #fff);
font: inherit;
font-weight: 700;
cursor: pointer;
}
.wire-next__collapse-trigger:focus-visible {
outline: 2px solid var(--wire-collapse-color);
.wrn-next__collapse-trigger:focus-visible {
outline: 2px solid var(--wrn-collapse-color);
outline-offset: 3px;
}
.wire-next__collapse-trigger:disabled {
.wrn-next__collapse-trigger:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.wire-next__collapse-chevron {
transition: transform var(--wire-motion-fast) var(--wire-ease-standard);
.wrn-next__collapse-chevron {
transition: transform var(--wrn-motion-fast) var(--wrn-ease-standard);
}
.wire-next__collapse-item[data-open="true"] .wire-next__collapse-chevron {
.wrn-next__collapse-item[data-open="true"] .wrn-next__collapse-chevron {
transform: rotate(180deg);
}
.wire-next__collapse-panel {
.wrn-next__collapse-panel {
width: min(100%, 42rem);
max-height: 0;
overflow: hidden;
margin-top: 0;
opacity: 0;
transition:
max-height 350ms var(--wire-ease-emphasized),
margin-top 300ms var(--wire-ease-emphasized),
opacity 200ms var(--wire-ease-standard);
max-height 350ms var(--wrn-ease-emphasized),
margin-top 300ms var(--wrn-ease-emphasized),
opacity 200ms var(--wrn-ease-standard);
}
.wire-next__collapse-panel[data-open="true"] {
.wrn-next__collapse-panel[data-open="true"] {
max-height: 40rem;
margin-top: 0.75rem;
opacity: 1;
}
.wire-next__collapse-content {
.wrn-next__collapse-content {
display: grid;
overflow: hidden;
min-height: 0;
gap: 0.65rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface-raised, var(--wire-color-surface));
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface-raised, var(--wrn-color-surface));
}
.wire-next__collapse-content h4,
.wire-next__collapse-content p,
.wire-next__collapse-preview {
.wrn-next__collapse-content h4,
.wrn-next__collapse-content p,
.wrn-next__collapse-preview {
margin: 0;
}
.wire-next__collapse-content nav {
.wrn-next__collapse-content nav {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
}
.wire-next__collapse-content a {
color: var(--wire-collapse-color);
.wrn-next__collapse-content a {
color: var(--wrn-collapse-color);
font-weight: 650;
text-decoration: none;
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-item {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-item {
gap: 0;
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-preview {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-preview {
order: 1;
margin-bottom: 0.5rem;
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-panel {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-panel {
order: 2;
width: 100%;
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-panel[data-open="true"] {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-panel[data-open="true"] {
margin-top: 0;
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-trigger {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-trigger {
order: 3;
margin-top: 0.5rem;
min-height: auto;
@@ -230,20 +227,89 @@ size: string = "default"
border: 0;
border-radius: 0;
background: transparent;
color: var(--wire-collapse-color);
color: var(--wrn-collapse-color);
}
.wire-next--collapse[data-mode="inline"] .wire-next__collapse-content {
.wrn-next--collapse[data-mode="inline"] .wrn-next__collapse-content {
padding: 0;
border: 0;
background: transparent;
}
@media (prefers-reduced-motion: reduce) {
.wire-next__collapse-chevron,
.wire-next__collapse-panel {
.wrn-next__collapse-chevron,
.wrn-next__collapse-panel {
transition: none;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+564 -11
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ColorPicker {
outputs {
input(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)
@@ -36,19 +32,576 @@ component ColorPicker {
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-component--color-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__color-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<input id="{id || name}" type="color" name="{name}" value="{value}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" /><output>{value}</output></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--field wrn-component--color-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wrn-next__field-heading"><label class="{hiddenLabel ? 'wrn-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wrn-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wrn-next__color-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<input id="{id || name}" type="color" name="{name}" value="{value}" disabled="{disabled}" required="{required}" readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" /><output>{value}</output></div>
{#if helperText}<small class="wrn-next__field-help">{helperText}</small>{/if}<small class="wrn-next__field-error" data-error="{name}">{error}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__color-control input[type="color"] {
.wrn-next__color-control input[type="color"] {
width: 3rem;
padding: 0.2rem;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+13 -13
View File
@@ -32,7 +32,7 @@ component Columns {
<div
{...attrs}
data-ui-component="Columns"
class='wire-columns {class}'
class='wrn-columns {class}'
data-size='{size}'
data-color='{color}'
data-gap='{gap}'
@@ -44,7 +44,7 @@ component Columns {
}
style {
.wire-columns {
.wrn-columns {
--columns-gap: 1.5rem;
display: grid;
width: 100%;
@@ -54,35 +54,35 @@ component Columns {
align-items: start;
}
.wire-columns[data-max-width="md"] {
.wrn-columns[data-max-width="md"] {
max-width: 48rem;
}
.wire-columns[data-max-width="lg"] {
.wrn-columns[data-max-width="lg"] {
max-width: 64rem;
}
.wire-columns[data-max-width="xl"] {
.wrn-columns[data-max-width="xl"] {
max-width: 80rem;
}
.wire-columns[data-max-width="2xl"] {
.wrn-columns[data-max-width="2xl"] {
max-width: 96rem;
}
.wire-columns[data-max-width="full"] {
.wrn-columns[data-max-width="full"] {
max-width: none;
}
.wire-columns[data-gap="sm"] {
.wrn-columns[data-gap="sm"] {
--columns-gap: 0.75rem;
}
.wire-columns[data-gap="lg"] {
.wrn-columns[data-gap="lg"] {
--columns-gap: 2.5rem;
}
.wire-columns[data-gap="xl"] {
.wrn-columns[data-gap="xl"] {
--columns-gap: 3.5rem;
}
@@ -91,17 +91,17 @@ component Columns {
* is worth keeping single-column for longer than a card deck is.
*/
@media (min-width: 768px) {
.wire-columns:not([data-columns="1"]) {
.wrn-columns:not([data-columns="1"]) {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (min-width: 1024px) {
.wire-columns[data-columns="3"] {
.wrn-columns[data-columns="3"] {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.wire-columns[data-columns="4"] {
.wrn-columns[data-columns="4"] {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
+94 -28
View File
@@ -1,7 +1,5 @@
import SelectStyles from "../styles/SelectStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ComboBox {
outputs {
search(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)
@@ -229,7 +227,7 @@ component ComboBox {
view {
<div
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--advanced-select wire-next--combobox {open ? 'wire-next--open' : ''} {fixed ? 'wire-next--advanced-select-fixed' : ''} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--advanced-select wrn-next--combobox {open ? 'wrn-next--open' : ''} {fixed ? 'wrn-next--advanced-select-fixed' : ''} {invalid ? 'wrn-next--invalid' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}"
data-placement="{placement}"
data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}"
@@ -245,12 +243,12 @@ component ComboBox {
>
<label id="{name}-label" for="{name}-input">{label}</label>
<div class="wire-next__select-control wire-next__combobox-control">
<span class="wire-next__search-icon icon-[lucide--search]" aria-hidden="true"></span>
<div class="wrn-next__select-control wrn-next__combobox-control">
<span class="wrn-next__search-icon icon-[lucide--search]" aria-hidden="true"></span>
<input
{...attrs}
id="{name}-input"
class="wire-next__select-trigger wire-next__combobox-input"
class="wrn-next__select-trigger wrn-next__combobox-input"
type="text"
role="combobox"
name="{allowCustomValue ? name : ''}"
@@ -273,7 +271,7 @@ component ComboBox {
<button
type="button"
class="wire-next__clear-select"
class="wrn-next__clear-select"
data-show="clearable && inputText() && !disabled"
aria-label="{clearLabel}"
title="{clearLabel}"
@@ -283,44 +281,44 @@ component ComboBox {
</button>
<button
type="button"
class="wire-next__combobox-toggle"
class="wrn-next__combobox-toggle"
aria-label="{toggleLabel}"
tabindex="-1"
disabled="{disabled}"
@click="toggle()"
>
<span class="wire-next__select-chevron icon-[lucide--chevrons-up-down]" aria-hidden="true"></span>
<span class="wrn-next__select-chevron icon-[lucide--chevrons-up-down]" aria-hidden="true"></span>
</button>
</div>
<div
class="wire-next__select-dropdown {fixed ? 'wire-next__select-dropdown--fixed' : ''}"
class="wrn-next__select-dropdown {fixed ? 'wrn-next__select-dropdown--fixed' : ''}"
data-show="shouldShowDropdown()"
style="{shouldShowDropdown() ? '' : 'display: none'}"
>
<div
class="wire-next__select-message"
class="wrn-next__select-message"
data-show="remote && query.length !== 0 && query.length < minSearchLength"
>Enter at least {minSearchLength} characters.</div>
<div class="wire-next__select-message" data-show="loading" role="status">
<i class="wire-spinner wire-spinner--inline" aria-hidden="true"></i>
<div class="wrn-next__select-message" data-show="loading" role="status">
<i class="wrn-spinner wrn-spinner--inline" aria-hidden="true"></i>
{loadingLabel}
</div>
<div
id="{name}-listbox"
class="wire-next__select-list"
class="wrn-next__select-list"
data-show="(!remote || (query.length === 0 && remoteAutoLoad) || query.length >= minSearchLength) && !loading"
role="listbox"
>
{#each groups as group}
{#if visibleOptions(group.options || []).length}
<div class="wire-next__option-group" role="group" aria-label="{group.label}">
<div class="wire-next__group-label">{group.label}</div>
<div class="wrn-next__option-group" role="group" aria-label="{group.label}">
<div class="wrn-next__group-label">{group.label}</div>
{#each group.options || [] as option}
<button
type="button"
class="wire-next__select-option {isSelected(option) ? 'wire-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wire-next__select-option--active' : ''}"
class="wrn-next__select-option {isSelected(option) ? 'wrn-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wrn-next__select-option--active' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
@@ -332,9 +330,9 @@ component ComboBox {
>
{#if optionTemplate === "icon" && option.icon}<i class="{option.icon}" aria-hidden="true"></i>{/if}
{#if optionTemplate === "avatar" && option.avatar}<img src="{option.avatar}" alt="" />{/if}
{#if optionTemplate === "color" && option.color}<i class="wire-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{#if optionTemplate === "color" && option.color}<i class="wrn-next__color-dot" style="--option-color: {option.color}"></i>{/if}
<span><strong>{option.label}</strong>{#if option.description}<small>{option.description}</small>{/if}</span>
<i class="wire-next__check icon-[lucide--check]" data-show="isSelected(option)" aria-hidden="true"></i>
<i class="wrn-next__check icon-[lucide--check]" data-show="isSelected(option)" aria-hidden="true"></i>
</button>
{/each}
</div>
@@ -344,7 +342,7 @@ component ComboBox {
{#each options as option}
<button
type="button"
class="wire-next__select-option {isSelected(option) ? 'wire-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wire-next__select-option--active' : ''}"
class="wrn-next__select-option {isSelected(option) ? 'wrn-next__select-option--selected' : ''} {optionIndex(option) === activeIndex ? 'wrn-next__select-option--active' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
@@ -356,16 +354,16 @@ component ComboBox {
>
{#if optionTemplate === "icon" && option.icon}<i class="{option.icon}" aria-hidden="true"></i>{/if}
{#if optionTemplate === "avatar" && option.avatar}<img src="{option.avatar}" alt="" />{/if}
{#if optionTemplate === "color" && option.color}<i class="wire-next__color-dot" style="--option-color: {option.color}"></i>{/if}
{#if optionTemplate === "color" && option.color}<i class="wrn-next__color-dot" style="--option-color: {option.color}"></i>{/if}
<span><strong>{option.label}</strong>{#if option.description}<small>{option.description}</small>{/if}</span>
<i class="wire-next__check icon-[lucide--check]" data-show="isSelected(option)" aria-hidden="true"></i>
<i class="wrn-next__check icon-[lucide--check]" data-show="isSelected(option)" aria-hidden="true"></i>
</button>
{/each}
</div>
<button
type="button"
class="wire-next__load-more"
class="wrn-next__load-more"
data-wrn-select-load-more
data-show="infinite && hasMore"
>{loadMoreLabel}</button>
@@ -385,16 +383,15 @@ component ComboBox {
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
class="wrn-next__validation"
data-error="{name}"
>{validationMessage}</small>
</div>
<SelectStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__combobox-toggle {
.wrn-next__combobox-toggle {
position: absolute;
top: 50%;
right: 0;
@@ -405,16 +402,85 @@ component ComboBox {
padding: 0;
place-items: center;
border: 0;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
background: transparent;
cursor: pointer;
transform: translateY(-50%);
}
.wire-next--combobox .wire-next__combobox-input {
.wrn-next--combobox .wrn-next__combobox-input {
padding-right: 5.25em;
padding-left: 2.5em;
cursor: text;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+15 -15
View File
@@ -20,7 +20,7 @@ component Confetti {
functions {
shared function pieces() { return Array.from({ length: Math.max(1, Math.min(60, count)) }) }
shared function pieceStyle(index) {
return "--wire-confetti-index:" + index + ";--wire-confetti-x:" + ((index * 37) % 100) + ";--wire-confetti-delay:" + ((index * 29) % 240) + "ms;"
return "--wrn-confetti-index:" + index + ";--wrn-confetti-x:" + ((index * 37) % 100) + ";--wrn-confetti-delay:" + ((index * 29) % 240) + "ms;"
}
client function finish(sourceEvent) {
running = false
@@ -41,11 +41,11 @@ component Confetti {
}
view {
<div {...attrs} data-ui-component="Confetti" data-running='{running ? "true" : "false"}' class='wire-confetti {class}' style='--wire-confetti-duration:{duration}ms'>
<button type="button" class="wire-confetti__trigger" disabled='{disabled}' @click='start(event)'>
<div {...attrs} data-ui-component="Confetti" data-running='{running ? "true" : "false"}' class='wrn-confetti {class}' style='--wrn-confetti-duration:{duration}ms'>
<button type="button" class="wrn-confetti__trigger" disabled='{disabled}' @click='start(event)'>
<span class="icon-[lucide--party-popper]" aria-hidden="true"></span><span>{label}</span>
</button>
<div class="wire-confetti__burst" aria-hidden="true">
<div class="wrn-confetti__burst" aria-hidden="true">
{#each pieces() as piece, index}<i style='{pieceStyle(index)}'></i>{/each}
</div>
<slot />
@@ -53,20 +53,20 @@ component Confetti {
}
style {
.wire-confetti { position: relative; display: inline-flex; }
.wire-confetti__trigger { display: inline-flex; min-height: 2.5rem; align-items: center; gap: 0.5rem; padding: 0.55rem 0.85rem; border: 1px solid var(--wire-color-primary); border-radius: var(--wire-radius); color: var(--wire-color-on-primary); background: var(--wire-color-primary); font: inherit; font-weight: 700; cursor: pointer; }
.wire-confetti__trigger > span:first-child { width: 1rem; height: 1rem; }
.wire-confetti__trigger:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: 2px; }
.wire-confetti__trigger:disabled { cursor: not-allowed; opacity: 0.55; }
.wire-confetti__burst { position: absolute; inset: 50% 0 auto; height: 0; pointer-events: none; }
.wire-confetti__burst > i { position: absolute; left: calc(var(--wire-confetti-x) * 1%); width: 0.45rem; height: 0.7rem; border-radius: 0.1rem; opacity: 0; background: hsl(calc(var(--wire-confetti-index) * 47deg) 80% 55%); transform: translate(-50%, 0) rotate(calc(var(--wire-confetti-index) * 23deg)); }
.wire-confetti[data-running="true"] .wire-confetti__burst > i { animation: wire-confetti-burst var(--wire-confetti-duration) cubic-bezier(0.2, 0.7, 0.2, 1) var(--wire-confetti-delay) both; }
@keyframes wire-confetti-burst {
.wrn-confetti { position: relative; display: inline-flex; }
.wrn-confetti__trigger { display: inline-flex; min-height: 2.5rem; align-items: center; gap: 0.5rem; padding: 0.55rem 0.85rem; border: 1px solid var(--wrn-color-primary); border-radius: var(--wrn-radius); color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); font: inherit; font-weight: 700; cursor: pointer; }
.wrn-confetti__trigger > span:first-child { width: 1rem; height: 1rem; }
.wrn-confetti__trigger:focus-visible { outline: 2px solid var(--wrn-color-focus); outline-offset: 2px; }
.wrn-confetti__trigger:disabled { cursor: not-allowed; opacity: 0.55; }
.wrn-confetti__burst { position: absolute; inset: 50% 0 auto; height: 0; pointer-events: none; }
.wrn-confetti__burst > i { position: absolute; left: calc(var(--wrn-confetti-x) * 1%); width: 0.45rem; height: 0.7rem; border-radius: 0.1rem; opacity: 0; background: hsl(calc(var(--wrn-confetti-index) * 47deg) 80% 55%); transform: translate(-50%, 0) rotate(calc(var(--wrn-confetti-index) * 23deg)); }
.wrn-confetti[data-running="true"] .wrn-confetti__burst > i { animation: wrn-confetti-burst var(--wrn-confetti-duration) cubic-bezier(0.2, 0.7, 0.2, 1) var(--wrn-confetti-delay) both; }
@keyframes wrn-confetti-burst {
0% { opacity: 1; transform: translate(-50%, 0) rotate(0); }
100% { opacity: 0; transform: translate(calc(-50% + (var(--wire-confetti-x) - 50) * 0.9px), 9rem) rotate(540deg); }
100% { opacity: 0; transform: translate(calc(-50% + (var(--wrn-confetti-x) - 50) * 0.9px), 9rem) rotate(540deg); }
}
@media (prefers-reduced-motion: reduce) {
.wire-confetti[data-running="true"] .wire-confetti__burst > i { animation-duration: 1ms; animation-delay: 0ms; }
.wrn-confetti[data-running="true"] .wrn-confetti__burst > i { animation-duration: 1ms; animation-delay: 0ms; }
}
}
}
+94 -94
View File
@@ -102,7 +102,7 @@ component ContextMenu {
}
client function moveFocus(sourceEvent, direction) {
const root = sourceEvent.currentTarget.closest(".wire-context-menu") || sourceEvent.currentTarget
const root = sourceEvent.currentTarget.closest(".wrn-context-menu") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-context-menu-item]:not([disabled])")]
if (!options.length) {
return
@@ -137,12 +137,12 @@ component ContextMenu {
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-context-menu {class}'
style='--wire-context-x:{positionX}px;--wire-context-y:{positionY}px;--wire-context-min-width:{minWidth};--wire-context-max-width:{maxWidth};'
class='wrn-context-menu {class}'
style='--wrn-context-x:{positionX}px;--wrn-context-y:{positionY}px;--wrn-context-min-width:{minWidth};--wrn-context-max-width:{maxWidth};'
@keydown='handleKeydown(event)'
>
<div
class="wire-context-menu__trigger"
class="wrn-context-menu__trigger"
tabindex='{disabled ? "-1" : "0"}'
aria-haspopup="menu"
aria-expanded='{open || visible ? "true" : "false"}'
@@ -156,7 +156,7 @@ component ContextMenu {
{#if closeOnOutside}
<button
type="button"
class="wire-context-menu__dismiss-layer"
class="wrn-context-menu__dismiss-layer"
data-show='{open || visible}'
aria-label="Close context menu"
@click='hideMenu("outside", event)'
@@ -164,14 +164,14 @@ component ContextMenu {
{/if}
<div
class="wire-context-menu__panel"
class="wrn-context-menu__panel"
data-wrn-anchored="true"
data-show='{open || visible}'
role="menu"
aria-label='{label}'
>
{#if title || description}
<div class="wire-context-menu__header">
<div class="wrn-context-menu__header">
{#if title}
<strong>{title}</strong>
{/if}
@@ -183,12 +183,12 @@ component ContextMenu {
<slot name="header"></slot>
<div class="wire-context-menu__items">
<div class="wrn-context-menu__items">
{#each items as item, itemIndex}
{#if item.type === "divider"}
<div class="wire-context-menu__divider" role="separator"></div>
<div class="wrn-context-menu__divider" role="separator"></div>
{:else if item.type === "header"}
<div class="wire-context-menu__section-label">{item.label || item.title}</div>
<div class="wrn-context-menu__section-label">{item.label || item.title}</div>
{:else if item.href}
<a
href='{item.href}'
@@ -199,13 +199,13 @@ component ContextMenu {
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}'
class="wire-context-menu__item"
class="wrn-context-menu__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-context-menu__icon {item.icon}' aria-hidden="true"></span>
<span class='wrn-context-menu__icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-context-menu__copy">
<span class="wrn-context-menu__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
@@ -214,9 +214,9 @@ component ContextMenu {
{#if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-context-menu__status" aria-hidden="true"></span>
<span class="icon-[lucide--check] wrn-context-menu__status" aria-hidden="true"></span>
{:else if item.external}
<span class="icon-[lucide--arrow-up-right] wire-context-menu__status" aria-hidden="true"></span>
<span class="icon-[lucide--arrow-up-right] wrn-context-menu__status" aria-hidden="true"></span>
{/if}
</a>
{:else}
@@ -227,13 +227,13 @@ component ContextMenu {
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
disabled='{item.disabled}'
class="wire-context-menu__item"
class="wrn-context-menu__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-context-menu__icon {item.icon}' aria-hidden="true"></span>
<span class='wrn-context-menu__icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-context-menu__copy">
<span class="wrn-context-menu__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
@@ -242,7 +242,7 @@ component ContextMenu {
{#if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-context-menu__status" aria-hidden="true"></span>
<span class="icon-[lucide--check] wrn-context-menu__status" aria-hidden="true"></span>
{/if}
</button>
{/if}
@@ -256,52 +256,52 @@ component ContextMenu {
}
style {
.wire-context-menu {
--context-accent: var(--wire-color-primary);
--context-soft: var(--wire-color-primary-soft);
.wrn-context-menu {
--context-accent: var(--wrn-color-primary);
--context-soft: var(--wrn-color-primary-soft);
position: relative;
display: block;
min-width: 0;
}
.wire-context-menu[data-color="secondary"] {
--context-accent: var(--wire-color-secondary);
--context-soft: var(--wire-color-secondary-soft);
.wrn-context-menu[data-color="secondary"] {
--context-accent: var(--wrn-color-secondary);
--context-soft: var(--wrn-color-secondary-soft);
}
.wire-context-menu[data-color="info"] {
--context-accent: var(--wire-color-info);
--context-soft: var(--wire-color-info-soft);
.wrn-context-menu[data-color="info"] {
--context-accent: var(--wrn-color-info);
--context-soft: var(--wrn-color-info-soft);
}
.wire-context-menu[data-color="success"] {
--context-accent: var(--wire-color-success);
--context-soft: var(--wire-color-success-soft);
.wrn-context-menu[data-color="success"] {
--context-accent: var(--wrn-color-success);
--context-soft: var(--wrn-color-success-soft);
}
.wire-context-menu[data-color="warning"] {
--context-accent: var(--wire-color-warning);
--context-soft: var(--wire-color-warning-soft);
.wrn-context-menu[data-color="warning"] {
--context-accent: var(--wrn-color-warning);
--context-soft: var(--wrn-color-warning-soft);
}
.wire-context-menu[data-color="danger"] {
--context-accent: var(--wire-color-danger);
--context-soft: var(--wire-color-danger-soft);
.wrn-context-menu[data-color="danger"] {
--context-accent: var(--wrn-color-danger);
--context-soft: var(--wrn-color-danger-soft);
}
.wire-context-menu__trigger {
.wrn-context-menu__trigger {
display: block;
min-width: 0;
outline: none;
}
.wire-context-menu__trigger:focus-visible {
.wrn-context-menu__trigger:focus-visible {
border-radius: 0.6rem;
outline: 2px solid var(--wire-color-focus);
outline: 2px solid var(--wrn-color-focus);
outline-offset: 3px;
}
.wire-context-menu__dismiss-layer {
.wrn-context-menu__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1090;
@@ -311,20 +311,20 @@ component ContextMenu {
border: 0;
}
.wire-context-menu__panel {
.wrn-context-menu__panel {
position: absolute;
z-index: 1091;
top: calc(100% + 0.5rem);
left: 0;
width: max-content;
min-width: var(--wire-context-min-width);
max-width: min(var(--wire-context-max-width), calc(100vw - 1.5rem));
min-width: var(--wrn-context-min-width);
max-width: min(var(--wrn-context-max-width), calc(100vw - 1.5rem));
padding: 0.45rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--context-accent) 5%, transparent), transparent 58%),
color-mix(in srgb, var(--wire-color-surface-raised) 96%, transparent);
border: 1px solid color-mix(in srgb, var(--context-accent) 18%, var(--wire-color-border));
color-mix(in srgb, var(--wrn-color-surface-raised) 96%, transparent);
border: 1px solid color-mix(in srgb, var(--context-accent) 18%, var(--wrn-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
@@ -333,25 +333,25 @@ component ContextMenu {
transform-origin: top left;
}
.wire-context-menu[data-placement="pointer"] .wire-context-menu__panel {
.wrn-context-menu[data-placement="pointer"] .wrn-context-menu__panel {
position: fixed;
top: var(--wire-context-y);
left: var(--wire-context-x);
top: var(--wrn-context-y);
left: var(--wrn-context-x);
}
.wire-context-menu[data-placement="bottom-end"] .wire-context-menu__panel {
.wrn-context-menu[data-placement="bottom-end"] .wrn-context-menu__panel {
right: 0;
left: auto;
transform-origin: top right;
}
.wire-context-menu[data-placement="top-start"] .wire-context-menu__panel {
.wrn-context-menu[data-placement="top-start"] .wrn-context-menu__panel {
top: auto;
bottom: calc(100% + 0.5rem);
transform-origin: bottom left;
}
.wire-context-menu[data-placement="top-end"] .wire-context-menu__panel {
.wrn-context-menu[data-placement="top-end"] .wrn-context-menu__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.5rem);
@@ -359,58 +359,58 @@ component ContextMenu {
transform-origin: bottom right;
}
.wire-context-menu[data-variant="soft"] .wire-context-menu__panel {
.wrn-context-menu[data-variant="soft"] .wrn-context-menu__panel {
background:
linear-gradient(145deg, var(--context-soft), transparent 72%),
var(--wire-color-surface-raised);
var(--wrn-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-context-menu[data-variant="outline"] .wire-context-menu__panel {
background: var(--wire-color-surface-raised);
.wrn-context-menu[data-variant="outline"] .wrn-context-menu__panel {
background: var(--wrn-color-surface-raised);
box-shadow: none;
}
.wire-context-menu__header {
.wrn-context-menu__header {
display: grid;
gap: 0.2rem;
padding: 0.65rem 0.75rem 0.75rem;
border-bottom: 1px solid var(--wire-color-border);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-context-menu__header strong {
.wrn-context-menu__header strong {
font-size: 0.86rem;
font-weight: 650;
}
.wire-context-menu__header span {
color: var(--wire-color-text-muted);
.wrn-context-menu__header span {
color: var(--wrn-color-text-muted);
font-size: 0.75rem;
line-height: 1.45;
}
.wire-context-menu__items {
.wrn-context-menu__items {
display: grid;
gap: 0.15rem;
padding-block: 0.25rem;
}
.wire-context-menu__section-label {
.wrn-context-menu__section-label {
padding: 0.55rem 0.75rem 0.3rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.wire-context-menu__divider {
.wrn-context-menu__divider {
height: 1px;
margin: 0.3rem 0.45rem;
background: var(--wire-color-border);
background: var(--wrn-color-border);
}
.wire-context-menu__item {
.wrn-context-menu__item {
appearance: none;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
@@ -419,7 +419,7 @@ component ContextMenu {
width: 100%;
min-width: 0;
padding: 0.65rem 0.7rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background: transparent;
border: 0;
border-radius: 0.72rem;
@@ -430,47 +430,47 @@ component ContextMenu {
transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.wire-context-menu__item:hover,
.wire-context-menu__item:focus-visible,
.wire-context-menu__item[data-selected="true"] {
.wrn-context-menu__item:hover,
.wrn-context-menu__item:focus-visible,
.wrn-context-menu__item[data-selected="true"] {
color: var(--context-accent);
background: var(--context-soft);
outline: none;
}
.wire-context-menu__item:active {
.wrn-context-menu__item:active {
transform: scale(0.985);
}
.wire-context-menu__item[data-danger="true"] {
color: var(--wire-color-danger);
.wrn-context-menu__item[data-danger="true"] {
color: var(--wrn-color-danger);
}
.wire-context-menu__item[data-danger="true"]:hover,
.wire-context-menu__item[data-danger="true"]:focus-visible {
background: var(--wire-color-danger-soft);
.wrn-context-menu__item[data-danger="true"]:hover,
.wrn-context-menu__item[data-danger="true"]:focus-visible {
background: var(--wrn-color-danger-soft);
}
.wire-context-menu__item[disabled],
.wire-context-menu__item[aria-disabled="true"] {
.wrn-context-menu__item[disabled],
.wrn-context-menu__item[aria-disabled="true"] {
opacity: 0.48;
pointer-events: none;
}
.wire-context-menu__icon,
.wire-context-menu__status {
.wrn-context-menu__icon,
.wrn-context-menu__status {
width: 1rem;
height: 1rem;
color: currentColor;
}
.wire-context-menu__copy {
.wrn-context-menu__copy {
display: grid;
gap: 0.12rem;
min-width: 0;
}
.wire-context-menu__copy strong {
.wrn-context-menu__copy strong {
overflow: hidden;
font-size: 0.82rem;
font-weight: 600;
@@ -479,35 +479,35 @@ component ContextMenu {
white-space: nowrap;
}
.wire-context-menu__copy small {
.wrn-context-menu__copy small {
overflow: hidden;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.7rem;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-context-menu kbd {
.wrn-context-menu kbd {
padding: 0.16rem 0.38rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.36rem;
font-size: 0.64rem;
font-family: inherit;
}
.wire-context-menu[data-size="sm"] .wire-context-menu__item {
.wrn-context-menu[data-size="sm"] .wrn-context-menu__item {
padding: 0.52rem 0.6rem;
}
.wire-context-menu[data-size="lg"] .wire-context-menu__item {
.wrn-context-menu[data-size="lg"] .wrn-context-menu__item {
padding: 0.78rem 0.82rem;
}
@media (max-width: 639px) {
.wire-context-menu[data-placement="pointer"] .wire-context-menu__panel {
.wrn-context-menu[data-placement="pointer"] .wrn-context-menu__panel {
right: 0.75rem;
bottom: 0.75rem;
left: 0.75rem;
@@ -518,7 +518,7 @@ component ContextMenu {
}
@media (prefers-reduced-motion: reduce) {
.wire-context-menu__item {
.wrn-context-menu__item {
transition: none;
}
}
+19 -19
View File
@@ -40,7 +40,7 @@ component CustomScrollbar {
<div
{...attrs}
data-ui-component="CustomScrollbar"
class='wire-scrollbar {class}'
class='wrn-scrollbar {class}'
data-color='{color}'
data-size='{size}'
data-axis='{axis}'
@@ -51,8 +51,8 @@ component CustomScrollbar {
}
style {
.wire-scrollbar {
--scrollbar-accent: var(--wire-color-primary);
.wrn-scrollbar {
--scrollbar-accent: var(--wrn-color-primary);
min-width: 0;
overflow: auto;
/*
@@ -64,48 +64,48 @@ component CustomScrollbar {
overscroll-behavior: contain;
}
.wire-scrollbar[data-color="secondary"] {
--scrollbar-accent: var(--wire-color-secondary);
.wrn-scrollbar[data-color="secondary"] {
--scrollbar-accent: var(--wrn-color-secondary);
}
.wire-scrollbar[data-color="success"] {
--scrollbar-accent: var(--wire-color-success);
.wrn-scrollbar[data-color="success"] {
--scrollbar-accent: var(--wrn-color-success);
}
.wire-scrollbar[data-color="danger"] {
--scrollbar-accent: var(--wire-color-danger);
.wrn-scrollbar[data-color="danger"] {
--scrollbar-accent: var(--wrn-color-danger);
}
.wire-scrollbar[data-color="info"] {
--scrollbar-accent: var(--wire-color-info);
.wrn-scrollbar[data-color="info"] {
--scrollbar-accent: var(--wrn-color-info);
}
.wire-scrollbar[data-axis="vertical"] {
.wrn-scrollbar[data-axis="vertical"] {
overflow-x: hidden;
overflow-y: auto;
}
.wire-scrollbar[data-axis="horizontal"] {
.wrn-scrollbar[data-axis="horizontal"] {
overflow-x: auto;
overflow-y: hidden;
}
.wire-scrollbar::-webkit-scrollbar {
.wrn-scrollbar::-webkit-scrollbar {
width: var(--scrollbar-thickness, 8px);
height: var(--scrollbar-thickness, 8px);
}
.wire-scrollbar::-webkit-scrollbar-track {
background: var(--wire-color-surface-soft);
.wrn-scrollbar::-webkit-scrollbar-track {
background: var(--wrn-color-surface-soft);
border-radius: var(--scrollbar-radius, 999px);
}
.wire-scrollbar::-webkit-scrollbar-thumb {
.wrn-scrollbar::-webkit-scrollbar-thumb {
background: color-mix(in srgb, var(--scrollbar-accent) 45%, transparent);
border-radius: var(--scrollbar-radius, 999px);
}
.wire-scrollbar::-webkit-scrollbar-thumb:hover {
.wrn-scrollbar::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-accent);
}
@@ -114,7 +114,7 @@ component CustomScrollbar {
* width above, so the region keeps its own padding instead.
*/
@media (hover: none) {
.wire-scrollbar {
.wrn-scrollbar {
scrollbar-width: auto;
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DataMap {
outputs {
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)
@@ -16,27 +14,95 @@ component DataMap {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--data-map wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--data-map wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--data-map {
.wrn-next--data-map {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--data-map > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--data-map > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--data-map > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--data-map > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--data-map > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--data-map > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+264 -198
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
//
// DataTable -- columns, sorting, filtering, pagination and selection over an
// array of records.
@@ -737,29 +735,29 @@ component DataTable {
data-layout='{layout}'
data-loading='{loading ? "true" : "false"}'
data-sticky-first='{stickyFirstColumn ? "true" : "false"}'
class='wire-table {class}'
class='wrn-table {class}'
@window:wrnexus:datatable:rows='applyRows(event)'
@window:wrnexus:datatable:error='applyError(event)'
>
{#if caption || description || searchable}
<div class="wire-table__toolbar">
<div class="wire-table__heading">
<div class="wrn-table__toolbar">
<div class="wrn-table__heading">
{#if caption}
<h3 class="wire-table__caption">{caption}</h3>
<h3 class="wrn-table__caption">{caption}</h3>
{/if}
{#if description}
<p class="wire-table__description">{description}</p>
<p class="wrn-table__description">{description}</p>
{/if}
</div>
<div class="wire-table__actions" data-show="visibleActions().length">
<span class="wire-table__selected-count" data-show="selectedCount()">
<div class="wrn-table__actions" data-show="visibleActions().length">
<span class="wrn-table__selected-count" data-show="selectedCount()">
{selectedCount()} selected
</span>
<button
type="button"
class="wire-table__action"
class="wrn-table__action"
data-for="entry in visibleActions()"
data-key="entry.id"
data-tone='{entry.tone}'
@@ -771,7 +769,7 @@ component DataTable {
</div>
{#if searchable}
<div class="wire-table__search">
<div class="wrn-table__search">
<svg
viewBox="0 0 24 24"
width="15"
@@ -797,15 +795,15 @@ component DataTable {
</div>
{/if}
<div class="wire-table__scroll">
<table class="wire-table__table" data-show="layout !== 'comparison'">
<div class="wrn-table__scroll">
<table class="wrn-table__table" data-show="layout !== 'comparison'">
<thead>
<tr>
{#if selectable}
<th class="wire-table__select-cell" scope="col">
<th class="wrn-table__select-cell" scope="col">
<button
type="button"
class="wire-table__check"
class="wrn-table__check"
role="checkbox"
aria-label="Select all rows on this page"
aria-checked='{allVisibleSelected() ? "true" : "false"}'
@@ -828,14 +826,14 @@ component DataTable {
>
<button
type="button"
class="wire-table__sort"
class="wrn-table__sort"
data-show="column.sortable"
data-active='{column.active}'
@click='sortBy(column.key)'
>
<span>{column.label}</span>
<svg
class="wire-table__sort-icon"
class="wrn-table__sort-icon"
viewBox="0 0 24 24"
width="12"
height="12"
@@ -863,10 +861,10 @@ component DataTable {
@click='emitRowClick(row.raw, event)'
>
{#if selectable}
<td class="wire-table__select-cell">
<td class="wrn-table__select-cell">
<button
type="button"
class="wire-table__check"
class="wrn-table__check"
role="checkbox"
aria-label="Select row"
aria-checked='{row.selected}'
@@ -893,7 +891,7 @@ component DataTable {
</table>
<!-- Comparison layout: fields down the left, one column per record. -->
<table class="wire-table__table wire-table__table--comparison" data-show="layout === 'comparison'">
<table class="wrn-table__table wrn-table__table--comparison" data-show="layout === 'comparison'">
<thead>
<tr>
<th scope="col">{caption}</th>
@@ -908,7 +906,7 @@ component DataTable {
</thead>
<tbody>
<tr data-for="field in comparisonRows()" data-key="field.key">
<th scope="row" class="wire-table__field-label">{field.label}</th>
<th scope="row" class="wrn-table__field-label">{field.label}</th>
<td
data-for="cell in field.cells"
data-key="cell.key"
@@ -921,26 +919,26 @@ component DataTable {
</tbody>
</table>
<div class="wire-table__status" data-show="loading">
<span class="wire-table__spinner" aria-hidden="true"></span>
<div class="wrn-table__status" data-show="loading">
<span class="wrn-table__spinner" aria-hidden="true"></span>
<span>{loadingLabel}</span>
</div>
<div class="wire-table__status wire-table__status--error" data-show="loadError">
<div class="wrn-table__status wrn-table__status--error" data-show="loadError">
<span>{loadError}</span>
<button type="button" class="wire-table__action" @click='reload()'>
<button type="button" class="wrn-table__action" @click='reload()'>
{retryLabel}
</button>
</div>
<div
class="wire-table__status"
class="wrn-table__status"
data-show="!loading && !loadError && totalCount() === 0"
>
<span>{emptyMessage()}</span>
<button
type="button"
class="wire-table__action"
class="wrn-table__action"
data-show="isFiltered()"
@click='clearSearch()'
>
@@ -949,11 +947,11 @@ component DataTable {
</div>
</div>
<div class="wire-table__footer" data-show="paginated && totalCount() > 0">
<p class="wire-table__range">{rangeLabel()}</p>
<div class="wrn-table__footer" data-show="paginated && totalCount() > 0">
<p class="wrn-table__range">{rangeLabel()}</p>
<div class="wire-table__pager">
<span class="wire-table__page-size">
<div class="wrn-table__pager">
<span class="wrn-table__page-size">
<span>Rows</span>
<!-- The shared Dropdown rather than a native select: a select
renders its list with the operating system styling, which
@@ -961,7 +959,7 @@ component DataTable {
slot so it stays reactive -- a prop would be fixed at the
value the dropdown was first rendered with. -->
<Dropdown
class="wire-table__page-dropdown"
class="wrn-table__page-dropdown"
width="sm"
size="sm"
placement="top-end"
@@ -975,7 +973,7 @@ component DataTable {
<button
type="button"
class="wire-table__page-button"
class="wrn-table__page-button"
aria-label="Previous page"
disabled='{currentPage() <= 1}'
@click='goToPage(currentPage() - 1)'
@@ -996,19 +994,19 @@ component DataTable {
</button>
<span
class="wire-table__page-indicator"
class="wrn-table__page-indicator"
data-show="paginationStyle !== 'numbered'"
>
{currentPage()} / {pageCount()}
</span>
<span
class="wire-table__page-numbers"
class="wrn-table__page-numbers"
data-show="paginationStyle === 'numbered'"
>
<button
type="button"
class="wire-table__page-number"
class="wrn-table__page-number"
data-for="entry in pageNumbers()"
data-key="entry.number"
data-active='{entry.active}'
@@ -1021,7 +1019,7 @@ component DataTable {
<button
type="button"
class="wire-table__page-button"
class="wrn-table__page-button"
aria-label="Next page"
disabled='{currentPage() >= pageCount()}'
@click='goToPage(currentPage() + 1)'
@@ -1045,48 +1043,47 @@ component DataTable {
<slot></slot>
</div>
<ComponentBaseStyles hidden />
}
style {
.wire-next--data-map {
.wrn-next--data-map {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
}
.wire-table {
--table-accent: var(--wire-color-primary);
.wrn-table {
--table-accent: var(--wrn-color-primary);
display: flex;
flex-direction: column;
gap: 0.85rem;
width: 100%;
min-width: 0;
max-width: 100%;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-table[data-color="secondary"] {
--table-accent: var(--wire-color-secondary);
.wrn-table[data-color="secondary"] {
--table-accent: var(--wrn-color-secondary);
}
.wire-table[data-color="success"] {
--table-accent: var(--wire-color-success);
.wrn-table[data-color="success"] {
--table-accent: var(--wrn-color-success);
}
.wire-table[data-color="danger"] {
--table-accent: var(--wire-color-danger);
.wrn-table[data-color="danger"] {
--table-accent: var(--wrn-color-danger);
}
.wire-table[data-color="info"] {
--table-accent: var(--wire-color-info);
.wrn-table[data-color="info"] {
--table-accent: var(--wrn-color-info);
}
.wire-table__toolbar {
.wrn-table__toolbar {
display: flex;
align-items: flex-end;
justify-content: space-between;
@@ -1094,97 +1091,97 @@ component DataTable {
gap: 0.75rem;
}
.wire-table__heading {
.wrn-table__heading {
display: grid;
gap: 0.2rem;
min-width: 0;
}
.wire-table__caption {
.wrn-table__caption {
margin: 0;
color: var(--wire-color-text);
color: var(--wrn-color-text);
font-size: 0.98rem;
font-weight: 650;
}
.wire-table__description {
.wrn-table__description {
margin: 0;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.82rem;
}
.wire-table__actions {
.wrn-table__actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.4rem;
}
.wire-table__selected-count {
color: var(--wire-color-text-muted);
.wrn-table__selected-count {
color: var(--wrn-color-text-muted);
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
.wire-table__action {
.wrn-table__action {
appearance: none;
display: inline-flex;
align-items: center;
gap: 0.35rem;
min-height: 2.1rem;
padding: 0 0.7rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
color: var(--wrn-color-text);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
font: inherit;
font-size: 0.79rem;
cursor: pointer;
transition: border-color 140ms ease, color 140ms ease, background 140ms ease;
}
.wire-table__action:hover {
.wrn-table__action:hover {
color: var(--table-accent);
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wire-color-border));
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wrn-color-border));
}
.wire-table__action[data-tone="danger"] {
color: var(--wire-color-danger);
border-color: color-mix(in srgb, var(--wire-color-danger) 35%, var(--wire-color-border));
.wrn-table__action[data-tone="danger"] {
color: var(--wrn-color-danger);
border-color: color-mix(in srgb, var(--wrn-color-danger) 35%, var(--wrn-color-border));
}
.wire-table__action[data-tone="danger"]:hover {
background: color-mix(in srgb, var(--wire-color-danger) 12%, transparent);
.wrn-table__action[data-tone="danger"]:hover {
background: color-mix(in srgb, var(--wrn-color-danger) 12%, transparent);
}
/* Full gridlines: vertical rules between columns as well as rows. */
.wire-table[data-gridlines="grid"] .wire-table__table th,
.wire-table[data-gridlines="grid"] .wire-table__table td {
border-right: 1px solid color-mix(in srgb, var(--wire-color-border) 60%, transparent);
.wrn-table[data-gridlines="grid"] .wrn-table__table th,
.wrn-table[data-gridlines="grid"] .wrn-table__table td {
border-right: 1px solid color-mix(in srgb, var(--wrn-color-border) 60%, transparent);
}
.wire-table[data-gridlines="grid"] .wire-table__table th:last-child,
.wire-table[data-gridlines="grid"] .wire-table__table td:last-child {
.wrn-table[data-gridlines="grid"] .wrn-table__table th:last-child,
.wrn-table[data-gridlines="grid"] .wrn-table__table td:last-child {
border-right: 0;
}
.wire-table__search {
.wrn-table__search {
display: inline-flex;
align-items: center;
gap: 0.45rem;
min-height: 2.35rem;
padding: 0 0.7rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.7rem;
}
.wire-table__search input {
.wrn-table__search input {
width: 12rem;
max-width: 100%;
padding: 0;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background: transparent;
border: 0;
font: inherit;
@@ -1192,8 +1189,8 @@ component DataTable {
outline: none;
}
.wire-table__search:focus-within {
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wire-color-border));
.wrn-table__search:focus-within {
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wrn-color-border));
}
/*
@@ -1205,73 +1202,73 @@ component DataTable {
* spilled out over the page beside it instead of scrolling inside its own
* box. min-width: 0 alone is not enough once the parent is a grid.
*/
.wire-table__scroll {
.wrn-table__scroll {
position: relative;
width: 100%;
min-width: 0;
max-width: 100%;
overflow-x: auto;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 0.9rem;
}
.wire-table[data-bordered="false"] .wire-table__scroll {
.wrn-table[data-bordered="false"] .wrn-table__scroll {
border: 0;
border-radius: 0;
}
.wire-table__table {
.wrn-table__table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
}
.wire-table__table th,
.wire-table__table td {
.wrn-table__table th,
.wrn-table__table td {
padding: 0.7rem 0.85rem;
text-align: left;
vertical-align: middle;
white-space: nowrap;
}
.wire-table[data-density="compact"] .wire-table__table th,
.wire-table[data-density="compact"] .wire-table__table td {
.wrn-table[data-density="compact"] .wrn-table__table th,
.wrn-table[data-density="compact"] .wrn-table__table td {
padding: 0.42rem 0.6rem;
}
.wire-table[data-density="comfortable"] .wire-table__table th,
.wire-table[data-density="comfortable"] .wire-table__table td {
.wrn-table[data-density="comfortable"] .wrn-table__table th,
.wrn-table[data-density="comfortable"] .wrn-table__table td {
padding: 0.95rem 1rem;
}
.wire-table__table th[data-align="center"],
.wire-table__table td[data-align="center"] {
.wrn-table__table th[data-align="center"],
.wrn-table__table td[data-align="center"] {
text-align: center;
}
.wire-table__table th[data-align="end"],
.wire-table__table td[data-align="end"] {
.wrn-table__table th[data-align="end"],
.wrn-table__table td[data-align="end"] {
text-align: right;
}
.wire-table__table thead th {
.wrn-table__table thead th {
position: sticky;
top: 0;
z-index: 1;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border-bottom: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border-bottom: 1px solid var(--wrn-color-border);
font-size: 0.76rem;
font-weight: 650;
letter-spacing: 0.02em;
text-transform: uppercase;
}
.wire-table__table tbody tr {
border-bottom: 1px solid color-mix(in srgb, var(--wire-color-border) 60%, transparent);
.wrn-table__table tbody tr {
border-bottom: 1px solid color-mix(in srgb, var(--wrn-color-border) 60%, transparent);
}
.wire-table__table tbody tr:last-child {
.wrn-table__table tbody tr:last-child {
border-bottom: 0;
}
@@ -1286,24 +1283,24 @@ component DataTable {
* about the row, not decoration, so it wins outright; every rule below
* sits at the same specificity and is ordered deliberately.
*/
.wire-table[data-striped="true"] .wire-table__table tbody tr:nth-child(even) {
background: color-mix(in srgb, var(--wire-color-surface-soft) 55%, transparent);
.wrn-table[data-striped="true"] .wrn-table__table tbody tr:nth-child(even) {
background: color-mix(in srgb, var(--wrn-color-surface-soft) 55%, transparent);
}
.wire-table[data-striped] .wire-table__table tbody tr:hover:not([data-selected="true"]) {
.wrn-table[data-striped] .wrn-table__table tbody tr:hover:not([data-selected="true"]) {
background: color-mix(in srgb, var(--table-accent) 7%, transparent);
}
/* Every selected row identical, striped or not. */
.wire-table[data-striped] .wire-table__table tbody tr[data-selected="true"] {
.wrn-table[data-striped] .wrn-table__table tbody tr[data-selected="true"] {
background: color-mix(in srgb, var(--table-accent) 15%, transparent);
}
.wire-table[data-striped] .wire-table__table tbody tr[data-selected="true"]:hover {
.wrn-table[data-striped] .wrn-table__table tbody tr[data-selected="true"]:hover {
background: color-mix(in srgb, var(--table-accent) 22%, transparent);
}
.wire-table__sort {
.wrn-table__sort {
appearance: none;
display: inline-flex;
align-items: center;
@@ -1320,29 +1317,29 @@ component DataTable {
cursor: pointer;
}
.wire-table__sort:hover {
.wrn-table__sort:hover {
color: var(--table-accent);
}
.wire-table__sort-icon {
.wrn-table__sort-icon {
flex: 0 0 auto;
opacity: 0.45;
}
.wire-table__sort[data-active="true"] {
.wrn-table__sort[data-active="true"] {
color: var(--table-accent);
}
.wire-table__sort[data-active="true"] .wire-table__sort-icon {
.wrn-table__sort[data-active="true"] .wrn-table__sort-icon {
opacity: 1;
}
.wire-table__select-cell {
.wrn-table__select-cell {
width: 2.6rem;
text-align: center;
}
.wire-table__check {
.wrn-table__check {
appearance: none;
display: inline-flex;
align-items: center;
@@ -1351,75 +1348,75 @@ component DataTable {
width: 1.05rem;
height: 1.05rem;
color: transparent;
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
background: var(--wrn-color-surface-raised);
border: 1px solid var(--wrn-color-border);
border-radius: 0.32rem;
cursor: pointer;
transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
}
.wire-table__check[aria-checked="true"] {
color: var(--wire-color-primary-contrast);
.wrn-table__check[aria-checked="true"] {
color: var(--wrn-color-primary-contrast);
background: var(--table-accent);
border-color: var(--table-accent);
}
.wire-table__check:focus-visible {
.wrn-table__check:focus-visible {
outline: none;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--table-accent) 35%, transparent);
}
.wire-table__table--comparison {
.wrn-table__table--comparison {
width: auto;
min-width: 100%;
}
.wire-table__table--comparison th,
.wire-table__table--comparison td {
.wrn-table__table--comparison th,
.wrn-table__table--comparison td {
min-width: 7rem;
}
.wire-table__table--comparison th[scope="row"] {
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
.wrn-table__table--comparison th[scope="row"] {
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
font-size: 0.76rem;
font-weight: 650;
text-transform: uppercase;
white-space: nowrap;
}
.wire-table[data-layout="comparison"] .wire-table__table th:first-child,
.wire-table[data-layout="comparison"] .wire-table__table td:first-child {
.wrn-table[data-layout="comparison"] .wrn-table__table th:first-child,
.wrn-table[data-layout="comparison"] .wrn-table__table td:first-child {
position: sticky;
left: 0;
z-index: 2;
}
.wire-table__status {
.wrn-table__status {
display: flex;
align-items: center;
justify-content: center;
gap: 0.6rem;
padding: 1.75rem 1rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.85rem;
text-align: center;
}
.wire-table__status--error {
color: var(--wire-color-danger);
.wrn-table__status--error {
color: var(--wrn-color-danger);
}
.wire-table__spinner {
.wrn-table__spinner {
width: 0.95rem;
height: 0.95rem;
border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
border-top-color: currentColor;
border-radius: 50%;
animation: wire-table-spin 700ms linear infinite;
animation: wrn-table-spin 700ms linear infinite;
}
@keyframes wire-table-spin {
@keyframes wrn-table-spin {
to {
transform: rotate(360deg);
}
@@ -1427,32 +1424,32 @@ component DataTable {
/* Dim the rows while a refresh is in flight rather than tearing them out:
a table that empties on every keystroke is far harder to read. */
.wire-table[data-loading="true"] .wire-table__table tbody {
.wrn-table[data-loading="true"] .wrn-table__table tbody {
opacity: 0.45;
}
/* Keeps the first column readable while the rest scrolls sideways. */
.wire-table[data-sticky-first="true"] .wire-table__table th:first-child,
.wire-table[data-sticky-first="true"] .wire-table__table td:first-child {
.wrn-table[data-sticky-first="true"] .wrn-table__table th:first-child,
.wrn-table[data-sticky-first="true"] .wrn-table__table td:first-child {
position: sticky;
left: 0;
z-index: 2;
background: var(--wire-color-surface);
background: var(--wrn-color-surface);
}
.wire-table[data-sticky-first="true"] .wire-table__table thead th:first-child {
.wrn-table[data-sticky-first="true"] .wrn-table__table thead th:first-child {
z-index: 3;
}
.wire-table__empty {
.wrn-table__empty {
margin: 0;
padding: 1.75rem 1rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.85rem;
text-align: center;
}
.wire-table__footer {
.wrn-table__footer {
display: flex;
align-items: center;
justify-content: space-between;
@@ -1460,27 +1457,27 @@ component DataTable {
gap: 0.75rem;
}
.wire-table__range {
.wrn-table__range {
margin: 0;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
}
.wire-table__pager {
.wrn-table__pager {
display: flex;
align-items: center;
gap: 0.5rem;
}
.wire-table__page-size {
.wrn-table__page-size {
display: inline-flex;
align-items: center;
gap: 0.4rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.78rem;
}
.wire-table__page-dropdown {
.wrn-table__page-dropdown {
display: inline-flex;
}
@@ -1489,7 +1486,7 @@ component DataTable {
* outranks the browser default and would crush the icon inside these
* fixed-size controls.
*/
.wire-table__page-button {
.wrn-table__page-button {
appearance: none;
display: inline-flex;
align-items: center;
@@ -1498,39 +1495,39 @@ component DataTable {
padding: 0;
width: 2rem;
height: 2rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.55rem;
cursor: pointer;
}
.wire-table__page-button:hover:not(:disabled) {
.wrn-table__page-button:hover:not(:disabled) {
color: var(--table-accent);
border-color: color-mix(in srgb, var(--table-accent) 40%, var(--wire-color-border));
border-color: color-mix(in srgb, var(--table-accent) 40%, var(--wrn-color-border));
}
.wire-table__page-button:disabled {
.wrn-table__page-button:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.wire-table__page-button svg {
.wrn-table__page-button svg {
flex: 0 0 auto;
}
.wire-table__page-numbers {
.wrn-table__page-numbers {
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.wire-table__page-number {
.wrn-table__page-number {
appearance: none;
min-width: 2rem;
height: 2rem;
padding: 0 0.4rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
background: transparent;
border: 1px solid transparent;
border-radius: 0.55rem;
@@ -1540,19 +1537,19 @@ component DataTable {
cursor: pointer;
}
.wire-table__page-number:hover {
color: var(--wire-color-text);
background: var(--wire-color-surface-soft);
.wrn-table__page-number:hover {
color: var(--wrn-color-text);
background: var(--wrn-color-surface-soft);
}
.wire-table__page-number[data-active="true"] {
color: var(--wire-color-primary-contrast);
.wrn-table__page-number[data-active="true"] {
color: var(--wrn-color-primary-contrast);
background: var(--table-accent);
border-color: var(--table-accent);
}
.wire-table__page-indicator {
color: var(--wire-color-text-muted);
.wrn-table__page-indicator {
color: var(--wrn-color-text-muted);
font-size: 0.78rem;
font-variant-numeric: tabular-nums;
}
@@ -1563,51 +1560,51 @@ component DataTable {
* scrollbar on a phone.
*/
@media (max-width: 639px) {
.wire-table__scroll {
.wrn-table__scroll {
overflow-x: visible;
border: 0;
}
.wire-table__table,
.wire-table__table tbody,
.wire-table__table tr,
.wire-table__table td {
.wrn-table__table,
.wrn-table__table tbody,
.wrn-table__table tr,
.wrn-table__table td {
display: block;
width: 100%;
}
.wire-table__table thead {
.wrn-table__table thead {
display: none;
}
/*
* Each row becomes a card. These are the exact tokens Card paints its
* surface with (.wire-next__card-panel in ui.css) rather than a lookalike
* surface with (.wrn-next__card-panel in ui.css) rather than a lookalike
* of my own, so a stacked row and a real Card cannot drift apart.
*
* The Card component itself cannot be mounted per row: component tags
* are resolved once, server side, while these rows are cloned in the
* browser from a single template -- every row would share one mount.
*/
.wire-table__table tbody tr {
.wrn-table__table tbody tr {
margin-bottom: 0.7rem;
padding: 0.5rem 0.25rem;
background: var(--wire-color-surface);
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
box-shadow: var(--wire-shadow-1);
background: var(--wrn-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
box-shadow: var(--wrn-shadow-1);
}
.wire-table[data-striped="true"] .wire-table__table tbody tr:nth-child(even) {
background: var(--wire-color-surface);
.wrn-table[data-striped="true"] .wrn-table__table tbody tr:nth-child(even) {
background: var(--wrn-color-surface);
}
.wire-table[data-striped] .wire-table__table tbody tr[data-selected="true"] {
background: color-mix(in srgb, var(--table-accent) 15%, var(--wire-color-surface));
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wire-color-border));
.wrn-table[data-striped] .wrn-table__table tbody tr[data-selected="true"] {
background: color-mix(in srgb, var(--table-accent) 15%, var(--wrn-color-surface));
border-color: color-mix(in srgb, var(--table-accent) 45%, var(--wrn-color-border));
}
.wire-table__table td {
.wrn-table__table td {
display: flex;
align-items: center;
justify-content: space-between;
@@ -1616,32 +1613,101 @@ component DataTable {
text-align: right;
}
.wire-table__table td::before {
.wrn-table__table td::before {
content: attr(data-label);
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.72rem;
font-weight: 650;
text-transform: uppercase;
}
.wire-table__select-cell {
.wrn-table__select-cell {
width: auto;
}
.wire-table__footer {
.wrn-table__footer {
justify-content: center;
}
.wire-table__actions {
.wrn-table__actions {
width: 100%;
}
/* The page-size menu is anchored to a control near the bottom of the
screen; it must be able to render outside the footer box. */
.wire-table__footer,
.wire-table__pager {
.wrn-table__footer,
.wrn-table__pager {
overflow: visible;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+583 -30
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DatePicker {
outputs {
input(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -49,77 +45,634 @@ size: string = "default"
client function openCalendar(sourceEvent) { expanded = true; output.open({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function updateDate(part, nextValue, sourceEvent, nextDate, detail) { if (part === "year") { selectedYear = String(nextValue) } if (part === "month") { selectedMonth = String(nextValue).padStart(2, "0") } if (part === "day") { selectedDay = String(nextValue).padStart(2, "0") } if (!selectedYear || !selectedMonth || !selectedDay) { return } nextDate = selectedYear + "-" + selectedMonth + "-" + selectedDay; if ((min && nextDate < min) || (max && nextDate > max)) { return } currentValue = nextDate; detail = { value: currentValue, year: selectedYear, month: selectedMonth, day: selectedDay, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function finishDate(sourceEvent) { if (!currentValue) { return } expanded = false; output.close({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function selectDay(sourceEvent, root, input, nextDate, trigger, detail) { root = sourceEvent.currentTarget.closest(".wire-next--date-picker"); input = root.querySelector(".wire-next__picker-value"); nextDate = input.getAttribute("value").slice(0, 5) + root.querySelector("select").getAttribute("value") + "-" + sourceEvent.currentTarget.dataset.value; currentValue = nextDate; input.setAttribute("value", nextDate); trigger = root.querySelector(".wire-next__date-trigger span"); if (trigger) { trigger.replaceChildren(nextDate) } sourceEvent.currentTarget.setAttribute("aria-pressed", "true"); detail = { value: nextDate, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function selectDay(sourceEvent, root, input, nextDate, trigger, detail) { root = sourceEvent.currentTarget.closest(".wrn-next--date-picker"); input = root.querySelector(".wrn-next__picker-value"); nextDate = input.getAttribute("value").slice(0, 5) + root.querySelector("select").getAttribute("value") + "-" + sourceEvent.currentTarget.dataset.value; currentValue = nextDate; input.setAttribute("value", nextDate); trigger = root.querySelector(".wrn-next__date-trigger span"); if (trigger) { trigger.replaceChildren(nextDate) } sourceEvent.currentTarget.setAttribute("aria-pressed", "true"); detail = { value: nextDate, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function clearDate(sourceEvent, detail) { if (disabled || readonly) { return } currentValue = ""; detail = { value: currentValue, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function handleFocus(sourceEvent) { output.focus({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function handleBlur(sourceEvent) { output.blur({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function handleInvalid(sourceEvent) { output.invalid({ name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--date-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}">
<div class="wire-next__field-heading"><label for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__picker-control"><input id="{id || name}" class="wire-next__sr-only wire-next__picker-value" type="text" name="{name}" value="{currentValue}" required="{required}" readonly aria-hidden="true" tabindex="-1" @invalid="handleInvalid(event)" /><button type="button" class="wire-next__date-trigger" disabled="{disabled || readonly}" aria-haspopup="dialog" aria-expanded="{expanded}" @click="openCalendar(event)" @focus="handleFocus(event)" @blur="handleBlur(event)"><span>{currentValue || placeholder || "Select date"}</span><i class="icon-[lucide--calendar-days]" aria-hidden="true"></i></button>{#if currentValue && !readonly && !disabled}<button type="button" class="wire-next__picker-clear" aria-label="Clear date" @click="clearDate(event)">×</button>{/if}</div>
<div class="wire-next__calendar" role="dialog" aria-label="{label}"><div class="wire-next__date-selectors"><label>Month<select value="{selectedMonth}" @change="updateDate('month', event.currentTarget.value, event)">{#each months as month}<option value="{month.value}" selected="{month.value === selectedMonth}">{month.label}</option>{/each}</select></label><label>Year<select value="{selectedYear}" @change="updateDate('year', event.currentTarget.value, event)">{#each years as year}<option value="{year}" selected="{String(year) === selectedYear}">{year}</option>{/each}</select></label></div><div class="wire-next__calendar-grid">{#each days as day}<button type="button" data-value="{String(day).padStart(2, '0')}" aria-pressed="{String(day).padStart(2, '0') === selectedDay}" @click="selectDay(event)">{day}</button>{/each}</div><button type="button" class="wire-next__date-done" disabled="{!currentValue}" @click="finishDate(event)">Done</button></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--field wrn-next--date-picker {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}">
<div class="wrn-next__field-heading"><label for="{id || name}">{label}</label>{#if cornerHint}<span class="wrn-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wrn-next__picker-control"><input id="{id || name}" class="wrn-next__sr-only wrn-next__picker-value" type="text" name="{name}" value="{currentValue}" required="{required}" readonly aria-hidden="true" tabindex="-1" @invalid="handleInvalid(event)" /><button type="button" class="wrn-next__date-trigger" disabled="{disabled || readonly}" aria-haspopup="dialog" aria-expanded="{expanded}" @click="openCalendar(event)" @focus="handleFocus(event)" @blur="handleBlur(event)"><span>{currentValue || placeholder || "Select date"}</span><i class="icon-[lucide--calendar-days]" aria-hidden="true"></i></button>{#if currentValue && !readonly && !disabled}<button type="button" class="wrn-next__picker-clear" aria-label="Clear date" @click="clearDate(event)">×</button>{/if}</div>
<div class="wrn-next__calendar" role="dialog" aria-label="{label}"><div class="wrn-next__date-selectors"><label>Month<select value="{selectedMonth}" @change="updateDate('month', event.currentTarget.value, event)">{#each months as month}<option value="{month.value}" selected="{month.value === selectedMonth}">{month.label}</option>{/each}</select></label><label>Year<select value="{selectedYear}" @change="updateDate('year', event.currentTarget.value, event)">{#each years as year}<option value="{year}" selected="{String(year) === selectedYear}">{year}</option>{/each}</select></label></div><div class="wrn-next__calendar-grid">{#each days as day}<button type="button" data-value="{String(day).padStart(2, '0')}" aria-pressed="{String(day).padStart(2, '0') === selectedDay}" @click="selectDay(event)">{day}</button>{/each}</div><button type="button" class="wrn-next__date-done" disabled="{!currentValue}" @click="finishDate(event)">Done</button></div>
{#if helperText}<small class="wrn-next__field-help">{helperText}</small>{/if}<small class="wrn-next__field-error" data-error="{name}">{error}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__picker-clear {
.wrn-next__picker-clear {
position: absolute;
right: 2.5rem;
border: 0;
background: transparent;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
}
.wire-next__date-selectors {
.wrn-next__date-selectors {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 0.65rem;
}
.wire-next__date-selectors label {
.wrn-next__date-selectors label {
display: grid;
gap: 0.3rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.78rem;
font-weight: 600;
}
.wire-next__date-selectors select {
.wrn-next__date-selectors select {
width: 100%;
min-height: 2.35rem;
padding: 0.45rem 0.65rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
}
.wire-next__date-done {
background: var(--wire-field-color) !important;
color: var(--wire-color-primary-contrast, #fff) !important;
.wrn-next__date-done {
background: var(--wrn-field-color) !important;
color: var(--wrn-color-primary-contrast, #fff) !important;
font-weight: 650;
}
.wire-next__calendar > header {
.wrn-next__calendar > header {
display: grid;
grid-template-columns: 2.25rem 1fr 2.25rem;
align-items: center;
text-align: center;
}
.wire-next__calendar-grid button[data-outside="true"] {
color: var(--wire-color-muted);
.wrn-next__calendar-grid button[data-outside="true"] {
color: var(--wrn-color-muted);
opacity: 0.55;
}
.wire-next__calendar-grid button[data-today="true"] {
box-shadow: inset 0 0 0 1px var(--wire-field-color);
.wrn-next__calendar-grid button[data-today="true"] {
box-shadow: inset 0 0 0 1px var(--wrn-field-color);
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+93 -27
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DeviceFrame {
outputs {
change(payload: { device: string; orientation: string; sourceEvent: Event })
@@ -28,83 +26,151 @@ component DeviceFrame {
client function rotateFrame(sourceEvent) { currentOrientation = currentOrientation === "portrait" ? "landscape" : "portrait"; output.rotate({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }); output.change({ device: device, orientation: currentOrientation, sourceEvent: sourceEvent }) }
}
view {
<section {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--device-frame wire-next--variant-{variant} {class}" data-device="{device}" data-orientation="{currentOrientation}">
<header><div>{#if title}<strong>{title}</strong>{/if}{#if description}<p>{description}</p>{/if}</div>{#if showToolbar}<div class="wire-next__device-actions" role="toolbar" aria-label="Preview device">{#each items as item}<button type="button" aria-pressed="{item.value === device}" @click="setDevice(item.value, event)">{item.label}</button>{/each}<button type="button" aria-label="Rotate preview" @click="rotateFrame(event)">↻</button></div>{/if}</header>
<div class="wire-next__device-shell">{#if src || srcdoc}<iframe title="{frameTitle}" src="{src}" srcdoc="{srcdoc}" allow="{allow}"></iframe>{:else}<div class="wire-next__device-content"><slot /></div>{/if}</div>
<section {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--device-frame wrn-next--variant-{variant} {class}" data-device="{device}" data-orientation="{currentOrientation}">
<header><div>{#if title}<strong>{title}</strong>{/if}{#if description}<p>{description}</p>{/if}</div>{#if showToolbar}<div class="wrn-next__device-actions" role="toolbar" aria-label="Preview device">{#each items as item}<button type="button" aria-pressed="{item.value === device}" @click="setDevice(item.value, event)">{item.label}</button>{/each}<button type="button" aria-label="Rotate preview" @click="rotateFrame(event)">↻</button></div>{/if}</header>
<div class="wrn-next__device-shell">{#if src || srcdoc}<iframe title="{frameTitle}" src="{src}" srcdoc="{srcdoc}" allow="{allow}"></iframe>{:else}<div class="wrn-next__device-content"><slot /></div>{/if}</div>
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--device-frame {
.wrn-next--device-frame {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
}
.wire-next--device-frame > header {
.wrn-next--device-frame > header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.wire-next__device-actions {
.wrn-next__device-actions {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.wire-next__device-actions button {
.wrn-next__device-actions button {
min-height: 2.25rem;
padding-inline: 0.7rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface-2);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface-2);
color: inherit;
}
.wire-next__device-shell {
.wrn-next__device-shell {
overflow: hidden;
width: min(100%, 24rem);
min-height: 28rem;
margin-inline: auto;
border: 0.65rem solid var(--wire-color-text);
border: 0.65rem solid var(--wrn-color-text);
border-radius: 2rem;
background: var(--wire-color-background);
transition: width var(--wire-motion-normal) var(--wire-ease-standard);
background: var(--wrn-color-background);
transition: width var(--wrn-motion-normal) var(--wrn-ease-standard);
}
.wire-next--device-frame[data-device="tablet"] .wire-next__device-shell {
.wrn-next--device-frame[data-device="tablet"] .wrn-next__device-shell {
width: min(100%, 42rem);
}
.wire-next--device-frame[data-device="desktop"] .wire-next__device-shell {
.wrn-next--device-frame[data-device="desktop"] .wrn-next__device-shell {
width: 100%;
border-width: 0.4rem;
border-radius: var(--wire-radius-md);
border-radius: var(--wrn-radius-md);
}
.wire-next--device-frame[data-orientation="landscape"] .wire-next__device-shell {
.wrn-next--device-frame[data-orientation="landscape"] .wrn-next__device-shell {
width: min(100%, 48rem);
min-height: 20rem;
}
.wire-next__device-shell :is(iframe, .wire-next__device-content) {
.wrn-next__device-shell :is(iframe, .wrn-next__device-content) {
width: 100%;
min-height: inherit;
border: 0;
}
@media (max-width: 640px) {
.wire-next--device-frame > header {
.wrn-next--device-frame > header {
align-items: stretch;
flex-direction: column;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component DragAndDrop {
outputs {
dragStart(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -20,27 +18,95 @@ component DragAndDrop {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--drag-and-drop wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--drag-and-drop wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--drag-and-drop {
.wrn-next--drag-and-drop {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--drag-and-drop > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--drag-and-drop > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--drag-and-drop > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--drag-and-drop > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--drag-and-drop > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--drag-and-drop > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+107 -107
View File
@@ -80,14 +80,14 @@ open: boolean = false
data-variant='{variant}'
data-overlay='{overlay ? "true" : "false"}'
data-scrollable='{scrollable ? "true" : "false"}'
class='wire-drawer {class}'
class='wrn-drawer {class}'
style='--drawer-duration: {duration}ms'
>
{#if triggerLabel}
<button
type="button"
class="wire-drawer__trigger"
class="wrn-drawer__trigger"
aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}'
@click='showDrawer(event)'
@@ -99,40 +99,40 @@ open: boolean = false
</button>
{/if}
<span class="wire-drawer__trigger-slot" @click='showDrawer(event)'>
<span class="wrn-drawer__trigger-slot" @click='showDrawer(event)'>
<slot name="trigger"></slot>
</span>
<div
class="wire-drawer__layer"
class="wrn-drawer__layer"
role="presentation"
@keydown='handleKeydown(event)'
>
{#if overlay}
<button
type="button"
class="wire-drawer__backdrop"
class="wrn-drawer__backdrop"
aria-label='{closeLabel}'
@click='if (closeOnBackdrop) { cancelDrawer(event) }'
></button>
{/if}
<section
class="wire-drawer__panel"
class="wrn-drawer__panel"
role="dialog"
aria-modal="true"
aria-label='{label || title}'
tabindex="-1"
>
<div class="wire-drawer__handle" aria-hidden="true"></div>
<div class="wrn-drawer__handle" aria-hidden="true"></div>
<header class="wire-drawer__header">
<div class="wire-drawer__heading">
<header class="wrn-drawer__header">
<div class="wrn-drawer__heading">
{#if icon}
<span class='wire-drawer__icon {icon}' aria-hidden="true"></span>
<span class='wrn-drawer__icon {icon}' aria-hidden="true"></span>
{/if}
<div class="wire-drawer__heading-copy">
<div class="wrn-drawer__heading-copy">
<slot name="header"></slot>
{#if title}
<h2>{title}</h2>
@@ -146,7 +146,7 @@ open: boolean = false
{#if showClose}
<button
type="button"
class="wire-drawer__close"
class="wrn-drawer__close"
aria-label='{closeLabel}'
@click='hideDrawer("close-button", event)'
>
@@ -155,11 +155,11 @@ open: boolean = false
{/if}
</header>
<div class="wire-drawer__body">
<div class="wrn-drawer__body">
<slot></slot>
</div>
<footer class="wire-drawer__footer">
<footer class="wrn-drawer__footer">
<slot name="footer"></slot>
</footer>
</section>
@@ -168,50 +168,50 @@ open: boolean = false
}
style {
.wire-drawer {
--drawer-accent: var(--wire-color-primary);
--drawer-soft: var(--wire-color-primary-soft);
.wrn-drawer {
--drawer-accent: var(--wrn-color-primary);
--drawer-soft: var(--wrn-color-primary-soft);
position: relative;
display: inline-flex;
}
.wire-drawer[data-color="secondary"] {
--drawer-accent: var(--wire-color-secondary);
--drawer-soft: var(--wire-color-secondary-soft);
.wrn-drawer[data-color="secondary"] {
--drawer-accent: var(--wrn-color-secondary);
--drawer-soft: var(--wrn-color-secondary-soft);
}
.wire-drawer[data-color="info"] {
--drawer-accent: var(--wire-color-info);
--drawer-soft: var(--wire-color-info-soft);
.wrn-drawer[data-color="info"] {
--drawer-accent: var(--wrn-color-info);
--drawer-soft: var(--wrn-color-info-soft);
}
.wire-drawer[data-color="success"] {
--drawer-accent: var(--wire-color-success);
--drawer-soft: var(--wire-color-success-soft);
.wrn-drawer[data-color="success"] {
--drawer-accent: var(--wrn-color-success);
--drawer-soft: var(--wrn-color-success-soft);
}
.wire-drawer[data-color="warning"] {
--drawer-accent: var(--wire-color-warning);
--drawer-soft: var(--wire-color-warning-soft);
.wrn-drawer[data-color="warning"] {
--drawer-accent: var(--wrn-color-warning);
--drawer-soft: var(--wrn-color-warning-soft);
}
.wire-drawer[data-color="danger"] {
--drawer-accent: var(--wire-color-danger);
--drawer-soft: var(--wire-color-danger-soft);
.wrn-drawer[data-color="danger"] {
--drawer-accent: var(--wrn-color-danger);
--drawer-soft: var(--wrn-color-danger-soft);
}
.wire-drawer__trigger,
.wire-drawer__trigger-slot {
.wrn-drawer__trigger,
.wrn-drawer__trigger-slot {
display: inline-flex;
align-items: center;
gap: 0.55rem;
}
.wire-drawer__trigger {
.wrn-drawer__trigger {
appearance: none;
min-height: 2.55rem;
padding: 0.65rem 1rem;
color: var(--wire-color-primary-contrast);
color: var(--wrn-color-primary-contrast);
background: var(--drawer-accent);
border: 0;
border-radius: 0.8rem;
@@ -228,7 +228,7 @@ open: boolean = false
* 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 {
.wrn-drawer__layer {
position: fixed;
inset: 0;
z-index: 1200;
@@ -241,7 +241,7 @@ open: boolean = false
visibility 0s linear var(--drawer-duration, 260ms);
}
.wire-drawer[data-open="true"] .wire-drawer__layer {
.wrn-drawer[data-open="true"] .wrn-drawer__layer {
visibility: visible;
opacity: 1;
transition:
@@ -249,7 +249,7 @@ open: boolean = false
visibility 0s linear 0s;
}
.wire-drawer__backdrop {
.wrn-drawer__backdrop {
position: absolute;
inset: 0;
z-index: 0;
@@ -261,11 +261,11 @@ open: boolean = false
pointer-events: auto;
}
.wire-drawer[data-overlay="false"] .wire-drawer__backdrop {
.wrn-drawer[data-overlay="false"] .wrn-drawer__backdrop {
display: none;
}
.wire-drawer__panel {
.wrn-drawer__panel {
position: relative;
z-index: 1;
display: flex;
@@ -273,15 +273,15 @@ open: boolean = false
width: min(28rem, calc(100vw - 1rem));
max-height: 100%;
margin-left: auto;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--drawer-accent) 8%, transparent),
transparent 34%
),
var(--wire-color-surface-raised);
border-left: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
var(--wrn-color-surface-raised);
border-left: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wrn-color-border));
box-shadow: -28px 0 80px color-mix(in srgb, black 28%, transparent);
pointer-events: auto;
overflow: hidden;
@@ -290,66 +290,66 @@ open: boolean = false
transition: transform var(--drawer-duration, 260ms) cubic-bezier(0.32, 0.72, 0, 1);
}
.wire-drawer[data-open="true"] .wire-drawer__panel {
.wrn-drawer[data-open="true"] .wrn-drawer__panel {
transform: none;
}
.wire-drawer[data-placement="left"] .wire-drawer__panel {
.wrn-drawer[data-placement="left"] .wrn-drawer__panel {
transform: translateX(-100%);
}
.wire-drawer[data-placement="top"] .wire-drawer__panel {
.wrn-drawer[data-placement="top"] .wrn-drawer__panel {
transform: translateY(-100%);
}
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
.wrn-drawer[data-placement="bottom"] .wrn-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 {
.wrn-drawer[data-open="true"][data-placement="left"] .wrn-drawer__panel,
.wrn-drawer[data-open="true"][data-placement="top"] .wrn-drawer__panel,
.wrn-drawer[data-open="true"][data-placement="bottom"] .wrn-drawer__panel {
transform: none;
}
@media (prefers-reduced-motion: reduce) {
.wire-drawer__layer,
.wire-drawer__panel {
.wrn-drawer__layer,
.wrn-drawer__panel {
transition: none;
}
}
.wire-drawer[data-size="sm"] .wire-drawer__panel {
.wrn-drawer[data-size="sm"] .wrn-drawer__panel {
width: min(22rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="lg"] .wire-drawer__panel {
.wrn-drawer[data-size="lg"] .wrn-drawer__panel {
width: min(38rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="xl"] .wire-drawer__panel {
.wrn-drawer[data-size="xl"] .wrn-drawer__panel {
width: min(52rem, calc(100vw - 1rem));
}
.wire-drawer[data-size="full"] .wire-drawer__panel {
.wrn-drawer[data-size="full"] .wrn-drawer__panel {
width: 100vw;
}
.wire-drawer[data-placement="left"] .wire-drawer__panel {
.wrn-drawer[data-placement="left"] .wrn-drawer__panel {
margin-right: auto;
margin-left: 0;
border-right: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
border-right: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wrn-color-border));
border-left: 0;
box-shadow: 28px 0 80px color-mix(in srgb, black 28%, transparent);
}
.wire-drawer[data-placement="top"] .wire-drawer__layer,
.wire-drawer[data-placement="bottom"] .wire-drawer__layer {
.wrn-drawer[data-placement="top"] .wrn-drawer__layer,
.wrn-drawer[data-placement="bottom"] .wrn-drawer__layer {
align-items: flex-start;
}
.wire-drawer[data-placement="top"] .wire-drawer__panel,
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
.wrn-drawer[data-placement="top"] .wrn-drawer__panel,
.wrn-drawer[data-placement="bottom"] .wrn-drawer__panel {
width: 100%;
max-height: min(80vh, 44rem);
margin: 0;
@@ -357,61 +357,61 @@ open: boolean = false
box-shadow: 0 24px 80px color-mix(in srgb, black 28%, transparent);
}
.wire-drawer[data-placement="top"] .wire-drawer__panel {
border-bottom: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
.wrn-drawer[data-placement="top"] .wrn-drawer__panel {
border-bottom: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wrn-color-border));
}
.wire-drawer[data-placement="bottom"] .wire-drawer__layer {
.wrn-drawer[data-placement="bottom"] .wrn-drawer__layer {
align-items: flex-end;
}
.wire-drawer[data-placement="bottom"] .wire-drawer__panel {
border-top: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wire-color-border));
.wrn-drawer[data-placement="bottom"] .wrn-drawer__panel {
border-top: 1px solid color-mix(in srgb, var(--drawer-accent) 18%, var(--wrn-color-border));
border-radius: 1.3rem 1.3rem 0 0;
}
.wire-drawer[data-variant="soft"] .wire-drawer__panel {
.wrn-drawer[data-variant="soft"] .wrn-drawer__panel {
background:
linear-gradient(145deg, var(--drawer-soft), transparent 65%),
var(--wire-color-surface-raised);
var(--wrn-color-surface-raised);
}
.wire-drawer[data-variant="solid"] .wire-drawer__panel {
color: var(--wire-color-primary-contrast);
.wrn-drawer[data-variant="solid"] .wrn-drawer__panel {
color: var(--wrn-color-primary-contrast);
background: var(--drawer-accent);
border-color: color-mix(in srgb, white 18%, transparent);
}
.wire-drawer__handle {
.wrn-drawer__handle {
display: none;
width: 2.75rem;
height: 0.28rem;
margin: 0.55rem auto 0;
background: color-mix(in srgb, var(--wire-color-text-muted) 48%, transparent);
background: color-mix(in srgb, var(--wrn-color-text-muted) 48%, transparent);
border-radius: 999px;
}
.wire-drawer[data-placement="bottom"] .wire-drawer__handle {
.wrn-drawer[data-placement="bottom"] .wrn-drawer__handle {
display: block;
}
.wire-drawer__header {
.wrn-drawer__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
padding: 1.35rem 1.35rem 1.1rem;
border-bottom: 1px solid var(--wire-color-border);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-drawer__heading {
.wrn-drawer__heading {
display: flex;
align-items: flex-start;
gap: 0.85rem;
min-width: 0;
}
.wire-drawer__icon {
.wrn-drawer__icon {
flex: 0 0 auto;
width: 1.25rem;
height: 1.25rem;
@@ -419,34 +419,34 @@ open: boolean = false
color: var(--drawer-accent);
}
.wire-drawer[data-variant="solid"] .wire-drawer__icon {
.wrn-drawer[data-variant="solid"] .wrn-drawer__icon {
color: currentColor;
}
.wire-drawer__heading-copy {
.wrn-drawer__heading-copy {
display: grid;
gap: 0.3rem;
min-width: 0;
}
.wire-drawer__heading-copy h2,
.wire-drawer__heading-copy p {
.wrn-drawer__heading-copy h2,
.wrn-drawer__heading-copy p {
margin: 0;
}
.wire-drawer__heading-copy h2 {
.wrn-drawer__heading-copy h2 {
font-size: 1.05rem;
font-weight: 650;
line-height: 1.3;
}
.wire-drawer__heading-copy p {
color: var(--wire-color-text-muted);
.wrn-drawer__heading-copy p {
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
line-height: 1.55;
}
.wire-drawer[data-variant="solid"] .wire-drawer__heading-copy p {
.wrn-drawer[data-variant="solid"] .wrn-drawer__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
@@ -456,7 +456,7 @@ open: boolean = false
* 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 {
.wrn-drawer__close {
appearance: none;
display: inline-flex;
align-items: center;
@@ -465,22 +465,22 @@ open: boolean = false
padding: 0;
width: 2.35rem;
height: 2.35rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.75rem;
cursor: pointer;
}
.wire-drawer__close:hover,
.wire-drawer__close:focus-visible {
.wrn-drawer__close:hover,
.wrn-drawer__close:focus-visible {
color: var(--drawer-accent);
border-color: color-mix(in srgb, var(--drawer-accent) 34%, var(--wire-color-border));
border-color: color-mix(in srgb, var(--drawer-accent) 34%, var(--wrn-color-border));
outline: none;
}
/* Never let the glyph be shrunk by the flex container. */
.wire-drawer__close svg {
.wrn-drawer__close svg {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
@@ -494,40 +494,40 @@ open: boolean = false
* :where() keeps the specificity low enough that any class the app puts on
* its own slot content still wins.
*/
.wire-drawer__body {
.wrn-drawer__body {
flex: 1 1 auto;
min-height: 0;
padding: 1.35rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-drawer__body :where(p, li, dd, dt, h1, h2, h3, h4, h5, h6, span, label, code) {
.wrn-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 {
.wrn-drawer[data-scrollable="true"] .wrn-drawer__body {
overflow: auto;
overscroll-behavior: contain;
}
.wire-drawer__footer {
.wrn-drawer__footer {
padding: 1rem 1.35rem 1.35rem;
border-top: 1px solid var(--wire-color-border);
border-top: 1px solid var(--wrn-color-border);
}
.wire-drawer__footer:empty {
.wrn-drawer__footer:empty {
display: none;
}
@media (max-width: 639px) {
.wire-drawer[data-placement="right"] .wire-drawer__panel,
.wire-drawer[data-placement="left"] .wire-drawer__panel {
.wrn-drawer[data-placement="right"] .wrn-drawer__panel,
.wrn-drawer[data-placement="left"] .wrn-drawer__panel {
width: min(24rem, 100vw);
}
.wire-drawer__header,
.wire-drawer__body,
.wire-drawer__footer {
.wrn-drawer__header,
.wrn-drawer__body,
.wrn-drawer__footer {
padding-inline: 1rem;
}
}
+109 -109
View File
@@ -82,7 +82,7 @@ items: unknown[] = []
}
client function moveFocus(sourceEvent, direction) {
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const root = sourceEvent.currentTarget.closest(".wrn-dropdown") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-dropdown-item]:not([disabled])")]
if (!options.length) {
return
@@ -110,14 +110,14 @@ items: unknown[] = []
moveFocus(sourceEvent, -1)
} else if (sourceEvent.key === "Home" && isOpen()) {
sourceEvent.preventDefault()
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const root = sourceEvent.currentTarget.closest(".wrn-dropdown") || sourceEvent.currentTarget
const first = root.querySelector("[data-dropdown-item]:not([disabled])")
if (first) {
first.focus()
}
} else if (sourceEvent.key === "End" && isOpen()) {
sourceEvent.preventDefault()
const root = sourceEvent.currentTarget.closest(".wire-dropdown") || sourceEvent.currentTarget
const root = sourceEvent.currentTarget.closest(".wrn-dropdown") || sourceEvent.currentTarget
const options = [...root.querySelectorAll("[data-dropdown-item]:not([disabled])")]
if (options.length) {
options[options.length - 1].focus()
@@ -136,12 +136,12 @@ items: unknown[] = []
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-dropdown {class}'
class='wrn-dropdown {class}'
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-dropdown__trigger"
class="wrn-dropdown__trigger"
disabled='{disabled}'
aria-haspopup="menu"
aria-expanded='{open || visible ? "true" : "false"}'
@@ -149,20 +149,20 @@ items: unknown[] = []
>
<slot name="trigger"></slot>
{#if icon}
<span class='wire-dropdown__trigger-icon {icon}' aria-hidden="true"></span>
<span class='wrn-dropdown__trigger-icon {icon}' aria-hidden="true"></span>
{/if}
{#if label}
<span class="wire-dropdown__trigger-label">{label}</span>
<span class="wrn-dropdown__trigger-label">{label}</span>
{/if}
{#if showChevron}
<span class="icon-[lucide--chevron-down] wire-dropdown__chevron" aria-hidden="true"></span>
<span class="icon-[lucide--chevron-down] wrn-dropdown__chevron" aria-hidden="true"></span>
{/if}
</button>
{#if closeOnOutside}
<button
type="button"
class="wire-dropdown__dismiss-layer"
class="wrn-dropdown__dismiss-layer"
data-show='{open || visible}'
aria-label="Close menu"
@click='hideMenu("outside", event)'
@@ -170,7 +170,7 @@ items: unknown[] = []
{/if}
<div
class="wire-dropdown__panel"
class="wrn-dropdown__panel"
data-wrn-anchored="true"
data-show='{open || visible}'
role="menu"
@@ -178,12 +178,12 @@ items: unknown[] = []
>
<slot name="header"></slot>
<div class="wire-dropdown__items">
<div class="wrn-dropdown__items">
{#each items as item, itemIndex}
{#if item.type === "divider"}
<div class="wire-dropdown__divider" role="separator"></div>
<div class="wrn-dropdown__divider" role="separator"></div>
{:else if item.type === "header"}
<div class="wire-dropdown__section-label">{item.label || item.title}</div>
<div class="wrn-dropdown__section-label">{item.label || item.title}</div>
{:else if item.href}
<a
href='{item.href}'
@@ -194,26 +194,26 @@ items: unknown[] = []
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
aria-disabled='{item.disabled ? "true" : "false"}'
class="wire-dropdown__item"
class="wrn-dropdown__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
<span class='wrn-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-dropdown__copy">
<span class="wrn-dropdown__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.badge}
<span class="wire-dropdown__badge">{item.badge}</span>
<span class="wrn-dropdown__badge">{item.badge}</span>
{:else if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-dropdown__status" aria-hidden="true"></span>
<span class="icon-[lucide--check] wrn-dropdown__status" aria-hidden="true"></span>
{:else if item.external}
<span class="icon-[lucide--arrow-up-right] wire-dropdown__status" aria-hidden="true"></span>
<span class="icon-[lucide--arrow-up-right] wrn-dropdown__status" aria-hidden="true"></span>
{/if}
</a>
{:else}
@@ -224,29 +224,29 @@ items: unknown[] = []
data-danger='{item.danger ? "true" : "false"}'
data-selected='{item.selected || item.checked ? "true" : "false"}'
disabled='{item.disabled}'
class="wire-dropdown__item"
class="wrn-dropdown__item"
@click='chooseItem(item, itemIndex, event)'
>
{#if item.icon}
<span class='wire-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
<span class='wrn-dropdown__item-icon {item.icon}' aria-hidden="true"></span>
{/if}
<span class="wire-dropdown__copy">
<span class="wrn-dropdown__copy">
<strong>{item.label || item.title}</strong>
{#if item.description}
<small>{item.description}</small>
{/if}
</span>
{#if item.badge}
<span class="wire-dropdown__badge">{item.badge}</span>
<span class="wrn-dropdown__badge">{item.badge}</span>
{:else if item.shortcut}
<kbd>{item.shortcut}</kbd>
{:else if item.checked || item.selected}
<span class="icon-[lucide--check] wire-dropdown__status" aria-hidden="true"></span>
<span class="icon-[lucide--check] wrn-dropdown__status" aria-hidden="true"></span>
{/if}
</button>
{/if}
{:empty}
<div class="wire-dropdown__empty">{emptyLabel}</div>
<div class="wrn-dropdown__empty">{emptyLabel}</div>
{/each}
</div>
@@ -257,40 +257,40 @@ items: unknown[] = []
}
style {
.wire-dropdown {
--dropdown-accent: var(--wire-color-primary);
--dropdown-soft: var(--wire-color-primary-soft);
.wrn-dropdown {
--dropdown-accent: var(--wrn-color-primary);
--dropdown-soft: var(--wrn-color-primary-soft);
position: relative;
display: inline-flex;
min-width: 0;
}
.wire-dropdown[data-color="secondary"] {
--dropdown-accent: var(--wire-color-secondary);
--dropdown-soft: var(--wire-color-secondary-soft);
.wrn-dropdown[data-color="secondary"] {
--dropdown-accent: var(--wrn-color-secondary);
--dropdown-soft: var(--wrn-color-secondary-soft);
}
.wire-dropdown[data-color="info"] {
--dropdown-accent: var(--wire-color-info);
--dropdown-soft: var(--wire-color-info-soft);
.wrn-dropdown[data-color="info"] {
--dropdown-accent: var(--wrn-color-info);
--dropdown-soft: var(--wrn-color-info-soft);
}
.wire-dropdown[data-color="success"] {
--dropdown-accent: var(--wire-color-success);
--dropdown-soft: var(--wire-color-success-soft);
.wrn-dropdown[data-color="success"] {
--dropdown-accent: var(--wrn-color-success);
--dropdown-soft: var(--wrn-color-success-soft);
}
.wire-dropdown[data-color="warning"] {
--dropdown-accent: var(--wire-color-warning);
--dropdown-soft: var(--wire-color-warning-soft);
.wrn-dropdown[data-color="warning"] {
--dropdown-accent: var(--wrn-color-warning);
--dropdown-soft: var(--wrn-color-warning-soft);
}
.wire-dropdown[data-color="danger"] {
--dropdown-accent: var(--wire-color-danger);
--dropdown-soft: var(--wire-color-danger-soft);
.wrn-dropdown[data-color="danger"] {
--dropdown-accent: var(--wrn-color-danger);
--dropdown-soft: var(--wrn-color-danger-soft);
}
.wire-dropdown__trigger {
.wrn-dropdown__trigger {
appearance: none;
display: inline-flex;
align-items: center;
@@ -298,9 +298,9 @@ items: unknown[] = []
gap: 0.55rem;
min-height: 2.55rem;
padding: 0.65rem 0.9rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text);
background: var(--wrn-color-surface-raised);
border: 1px solid var(--wrn-color-border);
border-radius: 0.78rem;
font: inherit;
font-size: 0.84rem;
@@ -309,47 +309,47 @@ items: unknown[] = []
transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
}
.wire-dropdown__trigger:hover,
.wire-dropdown__trigger:focus-visible,
.wire-dropdown[data-open="true"] .wire-dropdown__trigger {
.wrn-dropdown__trigger:hover,
.wrn-dropdown__trigger:focus-visible,
.wrn-dropdown[data-open="true"] .wrn-dropdown__trigger {
color: var(--dropdown-accent);
background: var(--dropdown-soft);
border-color: color-mix(in srgb, var(--dropdown-accent) 38%, var(--wire-color-border));
border-color: color-mix(in srgb, var(--dropdown-accent) 38%, var(--wrn-color-border));
outline: none;
}
.wire-dropdown__trigger:disabled {
.wrn-dropdown__trigger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.wire-dropdown__trigger-icon,
.wire-dropdown__chevron {
.wrn-dropdown__trigger-icon,
.wrn-dropdown__chevron {
width: 1rem;
height: 1rem;
}
.wire-dropdown__chevron {
.wrn-dropdown__chevron {
transition: transform 160ms ease;
}
.wire-dropdown[data-open="true"] .wire-dropdown__chevron {
.wrn-dropdown[data-open="true"] .wrn-dropdown__chevron {
transform: rotate(180deg);
}
.wire-dropdown[data-size="sm"] .wire-dropdown__trigger {
.wrn-dropdown[data-size="sm"] .wrn-dropdown__trigger {
min-height: 2.2rem;
padding: 0.5rem 0.72rem;
font-size: 0.78rem;
}
.wire-dropdown[data-size="lg"] .wire-dropdown__trigger {
.wrn-dropdown[data-size="lg"] .wrn-dropdown__trigger {
min-height: 2.9rem;
padding: 0.78rem 1.05rem;
font-size: 0.9rem;
}
.wire-dropdown__dismiss-layer {
.wrn-dropdown__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1100;
@@ -359,7 +359,7 @@ items: unknown[] = []
border: 0;
}
.wire-dropdown__panel {
.wrn-dropdown__panel {
position: absolute;
z-index: 1101;
top: calc(100% + 0.55rem);
@@ -368,11 +368,11 @@ items: unknown[] = []
min-width: 12rem;
max-width: min(22rem, calc(100vw - 1.5rem));
padding: 0.45rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--dropdown-accent) 5%, transparent), transparent 60%),
color-mix(in srgb, var(--wire-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--dropdown-accent) 18%, var(--wire-color-border));
color-mix(in srgb, var(--wrn-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--dropdown-accent) 18%, var(--wrn-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
@@ -380,73 +380,73 @@ items: unknown[] = []
backdrop-filter: blur(18px);
}
.wire-dropdown[data-width="trigger"] .wire-dropdown__panel {
.wrn-dropdown[data-width="trigger"] .wrn-dropdown__panel {
width: 100%;
min-width: 100%;
}
.wire-dropdown[data-width="sm"] .wire-dropdown__panel {
.wrn-dropdown[data-width="sm"] .wrn-dropdown__panel {
width: 12rem;
}
.wire-dropdown[data-width="md"] .wire-dropdown__panel {
.wrn-dropdown[data-width="md"] .wrn-dropdown__panel {
width: 17rem;
}
.wire-dropdown[data-width="lg"] .wire-dropdown__panel {
.wrn-dropdown[data-width="lg"] .wrn-dropdown__panel {
width: 22rem;
}
.wire-dropdown[data-placement="bottom-end"] .wire-dropdown__panel {
.wrn-dropdown[data-placement="bottom-end"] .wrn-dropdown__panel {
right: 0;
left: auto;
}
.wire-dropdown[data-placement="top-start"] .wire-dropdown__panel {
.wrn-dropdown[data-placement="top-start"] .wrn-dropdown__panel {
top: auto;
bottom: calc(100% + 0.55rem);
}
.wire-dropdown[data-placement="top-end"] .wire-dropdown__panel {
.wrn-dropdown[data-placement="top-end"] .wrn-dropdown__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.55rem);
left: auto;
}
.wire-dropdown[data-variant="soft"] .wire-dropdown__panel {
.wrn-dropdown[data-variant="soft"] .wrn-dropdown__panel {
background:
linear-gradient(145deg, var(--dropdown-soft), transparent 70%),
var(--wire-color-surface-raised);
var(--wrn-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-dropdown[data-variant="outline"] .wire-dropdown__panel {
background: var(--wire-color-surface-raised);
.wrn-dropdown[data-variant="outline"] .wrn-dropdown__panel {
background: var(--wrn-color-surface-raised);
box-shadow: none;
}
.wire-dropdown__items {
.wrn-dropdown__items {
display: grid;
gap: 0.15rem;
}
.wire-dropdown__section-label {
.wrn-dropdown__section-label {
padding: 0.55rem 0.72rem 0.3rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.wire-dropdown__divider {
.wrn-dropdown__divider {
height: 1px;
margin: 0.3rem 0.4rem;
background: var(--wire-color-border);
background: var(--wrn-color-border);
}
.wire-dropdown__item {
.wrn-dropdown__item {
appearance: none;
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
@@ -455,7 +455,7 @@ items: unknown[] = []
width: 100%;
min-width: 0;
padding: 0.68rem 0.72rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
background: transparent;
border: 0;
border-radius: 0.72rem;
@@ -466,46 +466,46 @@ items: unknown[] = []
transition: background-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.wire-dropdown__item:hover,
.wire-dropdown__item:focus-visible,
.wire-dropdown__item[data-selected="true"] {
.wrn-dropdown__item:hover,
.wrn-dropdown__item:focus-visible,
.wrn-dropdown__item[data-selected="true"] {
color: var(--dropdown-accent);
background: var(--dropdown-soft);
outline: none;
}
.wire-dropdown__item:active {
.wrn-dropdown__item:active {
transform: scale(0.985);
}
.wire-dropdown__item[data-danger="true"] {
color: var(--wire-color-danger);
.wrn-dropdown__item[data-danger="true"] {
color: var(--wrn-color-danger);
}
.wire-dropdown__item[data-danger="true"]:hover,
.wire-dropdown__item[data-danger="true"]:focus-visible {
background: var(--wire-color-danger-soft);
.wrn-dropdown__item[data-danger="true"]:hover,
.wrn-dropdown__item[data-danger="true"]:focus-visible {
background: var(--wrn-color-danger-soft);
}
.wire-dropdown__item[disabled],
.wire-dropdown__item[aria-disabled="true"] {
.wrn-dropdown__item[disabled],
.wrn-dropdown__item[aria-disabled="true"] {
opacity: 0.48;
pointer-events: none;
}
.wire-dropdown__item-icon,
.wire-dropdown__status {
.wrn-dropdown__item-icon,
.wrn-dropdown__status {
width: 1rem;
height: 1rem;
}
.wire-dropdown__copy {
.wrn-dropdown__copy {
display: grid;
gap: 0.12rem;
min-width: 0;
}
.wire-dropdown__copy strong {
.wrn-dropdown__copy strong {
overflow: hidden;
font-size: 0.82rem;
font-weight: 600;
@@ -514,16 +514,16 @@ items: unknown[] = []
white-space: nowrap;
}
.wire-dropdown__copy small {
.wrn-dropdown__copy small {
overflow: hidden;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.7rem;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-dropdown__badge {
.wrn-dropdown__badge {
padding: 0.18rem 0.45rem;
color: var(--dropdown-accent);
background: var(--dropdown-soft);
@@ -532,25 +532,25 @@ items: unknown[] = []
font-weight: 700;
}
.wire-dropdown kbd {
.wrn-dropdown kbd {
padding: 0.16rem 0.38rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.36rem;
font-size: 0.64rem;
font-family: inherit;
}
.wire-dropdown__empty {
.wrn-dropdown__empty {
padding: 1rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
text-align: center;
}
@media (max-width: 639px) {
.wire-dropdown__panel {
.wrn-dropdown__panel {
position: fixed;
right: 0.75rem;
bottom: 0.75rem;
@@ -563,9 +563,9 @@ items: unknown[] = []
}
@media (prefers-reduced-motion: reduce) {
.wire-dropdown__trigger,
.wire-dropdown__chevron,
.wire-dropdown__item {
.wrn-dropdown__trigger,
.wrn-dropdown__chevron,
.wrn-dropdown__item {
transition: none;
}
}
+568 -15
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component FileInput {
outputs {
input(payload: { files: File[]; name: string; sourceEvent: Event })
@@ -50,30 +46,587 @@ size: string = "default"
client function preventReadonly(sourceEvent) { if (readonly) sourceEvent.preventDefault() }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--file-input {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__file-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span>{selectedName || value || placeholder}</span><input id="{id || name}" type="file" name="{name}" accept="{accept}" multiple="{multiple}" disabled="{disabled}" required="{required}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="handleChange(event)" @change="handleChange(event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" @invalid="emitField('invalid', event)" /></div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--field wrn-next--file-input {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wrn-next__field-heading"><label class="{hiddenLabel ? 'wrn-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wrn-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wrn-next__file-control" data-icon-position="{iconPosition}">{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}<span>{selectedName || value || placeholder}</span><input id="{id || name}" type="file" name="{name}" accept="{accept}" multiple="{multiple}" disabled="{disabled}" required="{required}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" @click="preventReadonly(event)" @input="handleChange(event)" @change="handleChange(event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" @invalid="emitField('invalid', event)" /></div>
{#if helperText}<small class="wrn-next__field-help">{helperText}</small>{/if}<small class="wrn-next__field-error" data-error="{name}">{error}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__file-control {
.wrn-next__file-control {
position: relative;
min-height: 2.75rem;
padding: 0.65rem 0.85rem;
border: 1px dashed var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
border: 1px dashed var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wire-next__file-control input {
.wrn-next__file-control input {
position: absolute;
cursor: pointer;
opacity: 0;
inset: 0;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+578 -25
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component FileUploadProgress {
outputs {
cancel(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
@@ -34,60 +30,617 @@ size: string = "default"
client function completeUpload(sourceEvent) { currentValue = max; currentStatus = "complete"; output.complete(detail(sourceEvent)) }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--file-upload-progress {class}" data-status="{currentStatus}" aria-live="polite">
<div class="wire-next__row"><span>{#if fileName}<strong>{fileName}</strong><small>{#if uploadedSize || fileSize}{uploadedSize}{#if uploadedSize && fileSize} of {/if}{fileSize}{:else}{label}{/if}</small>{:else}<strong>{label}</strong>{/if}</span>{#if showValue}<strong>{percent()}%</strong>{/if}</div>
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--file-upload-progress {class}" data-status="{currentStatus}" aria-live="polite">
<div class="wrn-next__row"><span>{#if fileName}<strong>{fileName}</strong><small>{#if uploadedSize || fileSize}{uploadedSize}{#if uploadedSize && fileSize} of {/if}{fileSize}{:else}{label}{/if}</small>{:else}<strong>{label}</strong>{/if}</span>{#if showValue}<strong>{percent()}%</strong>{/if}</div>
<progress value="{currentValue}" max="{max}" aria-label="{label}"></progress>
<div class="wire-next__upload-status"><span class="wire-next__upload-status-icon" aria-hidden="true"></span><span>{#if currentStatus === "uploading"}Uploading securely…{:else if currentStatus === "complete"}Upload complete{:else if currentStatus === "error"}Upload failed{:else}Upload cancelled{/if}</span></div>
<div class="wire-next__upload-actions">{#if currentStatus === "uploading"}<button type="button" @click="cancelUpload(event)">{cancelLabel}</button><button type="button" @click="completeUpload(event)">Complete</button>{/if}{#if currentStatus === "error" || currentStatus === "cancelled"}<button type="button" @click="retryUpload(event)">{retryLabel}</button>{/if}</div>
<div class="wrn-next__upload-status"><span class="wrn-next__upload-status-icon" aria-hidden="true"></span><span>{#if currentStatus === "uploading"}Uploading securely…{:else if currentStatus === "complete"}Upload complete{:else if currentStatus === "error"}Upload failed{:else}Upload cancelled{/if}</span></div>
<div class="wrn-next__upload-actions">{#if currentStatus === "uploading"}<button type="button" @click="cancelUpload(event)">{cancelLabel}</button><button type="button" @click="completeUpload(event)">Complete</button>{/if}{#if currentStatus === "error" || currentStatus === "cancelled"}<button type="button" @click="retryUpload(event)">{retryLabel}</button>{/if}</div>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__row {
.wrn-next__row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.wire-next--file-upload-progress progress {
.wrn-next--file-upload-progress progress {
width: 100%;
accent-color: var(--wire-color-primary);
accent-color: var(--wrn-color-primary);
}
.wire-next--file-upload-progress {
.wrn-next--file-upload-progress {
display: grid;
width: min(100%, 36rem);
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
}
.wire-next--file-upload-progress progress {
.wrn-next--file-upload-progress progress {
width: 100%;
height: 0.55rem;
overflow: hidden;
border: 0;
border-radius: 999px;
accent-color: var(--wire-component-color);
accent-color: var(--wrn-component-color);
}
.wire-next__upload-actions {
.wrn-next__upload-actions {
justify-content: flex-end;
}
.wire-next__upload-actions button {
.wrn-next__upload-actions button {
min-height: 2.25rem;
padding-inline: 0.8rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface-2);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface-2);
color: inherit;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+19 -19
View File
@@ -26,14 +26,14 @@ component Image {
<figure
{...attrs}
data-ui-component="Image"
class='wire-image {class}'
class='wrn-image {class}'
data-size='{size}'
data-color='{color}'
data-fit='{fit}'
data-rounded='{rounded}'
>
<img
class="wire-image__img"
class="wrn-image__img"
data-show="src"
src='{src}'
alt='{alt}'
@@ -44,12 +44,12 @@ component Image {
/>
<div
class="wire-image__placeholder"
class="wrn-image__placeholder"
data-show="!src"
role="img"
aria-label='{alt || "Image placeholder"}'
>
<span class="icon-[lucide--image] wire-image__placeholder-icon" aria-hidden="true"></span>
<span class="icon-[lucide--image] wrn-image__placeholder-icon" aria-hidden="true"></span>
</div>
<slot></slot>
@@ -57,69 +57,69 @@ component Image {
}
style {
.wire-image {
.wrn-image {
position: relative;
margin: 0;
overflow: hidden;
width: 100%;
min-width: 0;
background: var(--wire-color-surface-soft);
background: var(--wrn-color-surface-soft);
}
.wire-image[data-rounded="true"] {
border-radius: var(--wire-radius-lg);
.wrn-image[data-rounded="true"] {
border-radius: var(--wrn-radius-lg);
}
/*
* The ratio sits on the frame rather than the image, so the space is
* reserved before the file arrives and the page does not jump.
*/
.wire-image[data-size="square"] {
.wrn-image[data-size="square"] {
aspect-ratio: 1 / 1;
}
.wire-image[data-size="video"] {
.wrn-image[data-size="video"] {
aspect-ratio: 16 / 9;
}
.wire-image[data-size="landscape"] {
.wrn-image[data-size="landscape"] {
aspect-ratio: 4 / 3;
}
.wire-image[data-size="portrait"] {
.wrn-image[data-size="portrait"] {
aspect-ratio: 3 / 4;
}
.wire-image__img {
.wrn-image__img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.wire-image[data-fit="contain"] .wire-image__img {
.wrn-image[data-fit="contain"] .wrn-image__img {
object-fit: contain;
}
.wire-image[data-fit="fill"] .wire-image__img {
.wrn-image[data-fit="fill"] .wrn-image__img {
object-fit: fill;
}
.wire-image[data-size="default"] .wire-image__img {
.wrn-image[data-size="default"] .wrn-image__img {
height: auto;
}
.wire-image__placeholder {
.wrn-image__placeholder {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 12rem;
height: 100%;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-image__placeholder-icon {
.wrn-image__placeholder-icon {
width: 2rem;
height: 2rem;
}
+576 -23
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component InputGroup {
outputs {
input(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)
@@ -43,50 +39,607 @@ component InputGroup {
client function handleAction(sourceEvent) { output.action({ name: name, sourceEvent: sourceEvent }) }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--input-group {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__input-group-control">
{#if startText}<span class="wire-next__input-addon">{startText}</span>{/if}
{#if icon}<span class="{icon} wire-next__field-icon" data-position="{iconPosition}" aria-hidden="true"></span>{/if}
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--field wrn-next--input-group {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wrn-next__field-heading"><label class="{hiddenLabel ? 'wrn-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wrn-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wrn-next__input-group-control">
{#if startText}<span class="wrn-next__input-addon">{startText}</span>{/if}
{#if icon}<span class="{icon} wrn-next__field-icon" data-position="{iconPosition}" aria-hidden="true"></span>{/if}
<input id="{id || name}" type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" readonly="{readonly}" disabled="{disabled}" required="{required}" aria-invalid="{error ? 'true' : 'false'}" @input="emitField('input', event)" @change="emitField('change', event)" @focus="emitField('focus', event)" @blur="emitField('blur', event)" @keydown="handleKeydown(event)" />
{#if endText}<span class="wire-next__input-addon">{endText}</span>{/if}
{#if endText}<span class="wrn-next__input-addon">{endText}</span>{/if}
{#if actionLabel}<button type="button" disabled="{disabled}" @click="handleAction(event)">{actionLabel}</button>{/if}
</div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
{#if helperText}<small class="wrn-next__field-help">{helperText}</small>{/if}<small class="wrn-next__field-error" data-error="{name}">{error}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__input-group-control {
.wrn-next__input-group-control {
overflow: hidden;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wire-next__input-group-control input {
.wrn-next__input-group-control input {
flex: 1;
min-width: 0;
border: 0;
box-shadow: none;
}
.wire-next__input-addon,
.wire-next__input-group-control button {
.wrn-next__input-addon,
.wrn-next__input-group-control button {
padding: 0.7rem 0.85rem;
white-space: nowrap;
}
.wire-next__input-group-control button {
.wrn-next__input-group-control button {
align-self: stretch;
border: 0;
background: var(--wire-field-color);
color: var(--wire-color-primary-contrast, #fff);
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
font: inherit;
font-weight: 700;
cursor: pointer;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+84 -84
View File
@@ -81,26 +81,26 @@ component InputNumber {
shared function componentColor() {
if (color === "secondary") {
return "var(--wire-color-secondary)"
return "var(--wrn-color-secondary)"
}
if (color === "success") {
return "var(--wire-color-success)"
return "var(--wrn-color-success)"
}
if (color === "warning") {
return "var(--wire-color-warning)"
return "var(--wrn-color-warning)"
}
if (color === "danger") {
return "var(--wire-color-danger)"
return "var(--wrn-color-danger)"
}
if (color === "info") {
return "var(--wire-color-info)"
return "var(--wrn-color-info)"
}
return "var(--wire-color-primary)"
return "var(--wrn-color-primary)"
}
shared function hasMin() {
@@ -420,18 +420,18 @@ component InputNumber {
data-disabled='{disabled ? "true" : "false"}'
data-full-width='{fullWidth ? "true" : "false"}'
style='--input-number-accent: {componentColor()};'
class='wire-input-number {class}'
class='wrn-input-number {class}'
>
{#if label !== "" && variant !== "labeled" && variant !== "seat"}
<label
for='{inputId()}'
class='wire-input-number__label'
class='wrn-input-number__label'
>
<span>{label}</span>
{#if required}
<span
aria-hidden="true"
class='wire-input-number__required'
class='wrn-input-number__required'
>*</span>
{/if}
</label>
@@ -440,14 +440,14 @@ component InputNumber {
{#if description !== "" && variant !== "labeled" && variant !== "seat"}
<p
id='{descriptionId()}'
class='wire-input-number__description'
class='wrn-input-number__description'
>
{description}
</p>
{/if}
<div
class='wire-input-number__control'
class='wrn-input-number__control'
>
{#if variant === "horizontal" && showButtons}
<button
@@ -456,30 +456,30 @@ component InputNumber {
aria-controls='{inputId()}'
disabled='{decrementDisabled()}'
@click='decrementValue(event)'
class='wire-input-number__button wire-input-number__button--start'
class='wrn-input-number__button wrn-input-number__button--start'
>
<span
aria-hidden="true"
class='icon-[lucide--minus] wire-input-number__icon'
class='icon-[lucide--minus] wrn-input-number__icon'
></span>
</button>
{/if}
<div
class='wire-input-number__content'
class='wrn-input-number__content'
>
{#if variant === "labeled" || variant === "seat"}
<div class='wire-input-number__copy'>
<div class='wrn-input-number__copy'>
{#if label !== ""}
<label
for='{inputId()}'
class='wire-input-number__inner-label'
class='wrn-input-number__inner-label'
>
{label}
{#if required}
<span
aria-hidden="true"
class='wire-input-number__required'
class='wrn-input-number__required'
>*</span>
{/if}
</label>
@@ -488,7 +488,7 @@ component InputNumber {
{#if description !== ""}
<span
id='{descriptionId()}'
class='wire-input-number__inner-description'
class='wrn-input-number__inner-description'
>
{description}
</span>
@@ -497,12 +497,12 @@ component InputNumber {
{/if}
<div
class='wire-input-number__field'
class='wrn-input-number__field'
>
{#if prefix !== ""}
<span
aria-hidden="true"
class='wire-input-number__prefix'
class='wrn-input-number__prefix'
>
{prefix}
</span>
@@ -532,13 +532,13 @@ component InputNumber {
@change='handleChange(event)'
@keydown='handleKeydown(event)'
@wheel='handleWheel(event)'
class='wire-input-number__input'
class='wrn-input-number__input'
/>
{#if suffix !== ""}
<span
aria-hidden="true"
class='wire-input-number__suffix'
class='wrn-input-number__suffix'
>
{suffix}
</span>
@@ -553,11 +553,11 @@ component InputNumber {
aria-controls='{inputId()}'
disabled='{incrementDisabled()}'
@click='incrementValue(event)'
class='wire-input-number__button wire-input-number__button--end'
class='wrn-input-number__button wrn-input-number__button--end'
>
<span
aria-hidden="true"
class='icon-[lucide--plus] wire-input-number__icon'
class='icon-[lucide--plus] wrn-input-number__icon'
></span>
</button>
{:else}
@@ -565,7 +565,7 @@ component InputNumber {
<div
role="group"
aria-label='{controlsLabel}'
class='wire-input-number__buttons'
class='wrn-input-number__buttons'
>
<button
type="button"
@@ -573,11 +573,11 @@ component InputNumber {
aria-controls='{inputId()}'
disabled='{decrementDisabled()}'
@click='decrementValue(event)'
class='wire-input-number__button wire-input-number__button--decrement'
class='wrn-input-number__button wrn-input-number__button--decrement'
>
<span
aria-hidden="true"
class='icon-[lucide--minus] wire-input-number__icon'
class='icon-[lucide--minus] wrn-input-number__icon'
></span>
</button>
@@ -587,11 +587,11 @@ component InputNumber {
aria-controls='{inputId()}'
disabled='{incrementDisabled()}'
@click='incrementValue(event)'
class='wire-input-number__button wire-input-number__button--increment'
class='wrn-input-number__button wrn-input-number__button--increment'
>
<span
aria-hidden="true"
class='icon-[lucide--plus] wire-input-number__icon'
class='icon-[lucide--plus] wrn-input-number__icon'
></span>
</button>
</div>
@@ -604,11 +604,11 @@ component InputNumber {
id='{messageId()}'
role="alert"
aria-live="polite"
class='wire-input-number__message wire-input-number__message--error'
class='wrn-input-number__message wrn-input-number__message--error'
>
<span
aria-hidden="true"
class='icon-[lucide--circle-alert] wire-input-number__error-icon'
class='icon-[lucide--circle-alert] wrn-input-number__error-icon'
></span>
<span>{validationMessage()}</span>
</p>
@@ -617,7 +617,7 @@ component InputNumber {
{#if (!showValidationMessage || !isInvalid() || validationMessage() === "") && helpText !== ""}
<p
id='{messageId()}'
class='wire-input-number__message'
class='wrn-input-number__message'
>
{helpText}
</p>
@@ -626,57 +626,57 @@ component InputNumber {
}
style {
.wire-input-number { position: relative; display: flex; width: 100%; max-width: 24rem; flex-direction: column; gap: 0.375rem; color: var(--wire-color-text); }
.wire-input-number[data-full-width="true"] { max-width: none; }
.wire-input-number[data-variant="compact"] { width: fit-content; max-width: 100%; }
.wire-input-number[data-disabled="true"] { opacity: 0.6; }
.wire-input-number__label { display: inline-flex; align-items: center; gap: 0.25rem; color: var(--wire-color-text); font-size: 0.875rem; font-weight: 600; line-height: 1.25rem; }
.wire-input-number__required { margin-left: 0.125rem; color: var(--wire-color-danger); }
.wire-input-number__description, .wire-input-number__message { margin: 0; color: var(--wire-color-muted); font-size: 0.75rem; line-height: 1.25rem; }
.wire-input-number__control { display: flex; min-width: 0; min-height: 2.5rem; overflow: hidden; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-sm); color: var(--wire-color-text); background: var(--wire-color-surface); box-shadow: var(--wire-shadow-1); transition: border-color var(--wire-motion-base), box-shadow var(--wire-motion-base), background var(--wire-motion-base); }
.wire-input-number__control:focus-within { border-color: var(--input-number-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--input-number-accent) 18%, transparent); }
.wire-input-number[data-invalid="true"] .wire-input-number__control { border-color: var(--wire-color-danger); }
.wire-input-number[data-invalid="true"] .wire-input-number__control:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-color-danger) 18%, transparent); }
.wire-input-number[data-disabled="true"] .wire-input-number__control { cursor: not-allowed; background: var(--wire-color-surface-2); }
.wire-input-number[data-variant="compact"] .wire-input-number__control { border-radius: 999px; }
.wire-input-number[data-size="xs"] .wire-input-number__control { min-height: 2rem; font-size: 0.75rem; }
.wire-input-number[data-size="sm"] .wire-input-number__control { min-height: 2.25rem; font-size: 0.875rem; }
.wire-input-number[data-size="lg"] .wire-input-number__control { min-height: 3rem; font-size: 1rem; }
.wire-input-number[data-size="xl"] .wire-input-number__control { min-height: 3.5rem; font-size: 1.125rem; }
.wire-input-number__content { display: flex; min-width: 0; flex: 1; align-items: center; padding-inline: 0.75rem; }
.wire-input-number[data-size="xs"] .wire-input-number__content { padding-inline: 0.5rem; }
.wire-input-number[data-size="sm"] .wire-input-number__content { padding-inline: 0.625rem; }
.wire-input-number[data-size="lg"] .wire-input-number__content { padding-inline: 1rem; }
.wire-input-number[data-size="xl"] .wire-input-number__content { padding-inline: 1.25rem; }
.wire-input-number[data-variant="labeled"] .wire-input-number__content { flex-direction: column; align-items: stretch; justify-content: center; gap: 0.125rem; }
.wire-input-number[data-variant="seat"] .wire-input-number__content { justify-content: space-between; gap: 0.75rem; }
.wire-input-number__copy { min-width: 0; flex: 1; }
.wire-input-number__inner-label, .wire-input-number__inner-description { display: block; overflow: hidden; color: var(--wire-color-text); font-size: 0.875rem; font-weight: 600; line-height: 1rem; text-overflow: ellipsis; white-space: nowrap; }
.wire-input-number__inner-description { color: var(--wire-color-muted); font-size: 0.75rem; font-weight: 400; }
.wire-input-number[data-variant="labeled"] .wire-input-number__inner-label { color: var(--wire-color-muted); font-size: 0.75rem; font-weight: 500; }
.wire-input-number__field { display: flex; width: 100%; min-width: 0; align-items: center; }
.wire-input-number[data-variant="seat"] .wire-input-number__field { width: auto; flex: none; }
.wire-input-number__prefix, .wire-input-number__suffix { flex: none; color: var(--wire-color-muted); }
.wire-input-number__prefix { padding-right: 0.375rem; } .wire-input-number__suffix { padding-left: 0.375rem; }
.wire-input-number__input { width: 100%; min-width: 0; flex: 1; padding: 0; border: 0; outline: none; color: var(--wire-color-text); background: transparent; font: inherit; font-weight: 500; line-height: 1; appearance: textfield; }
.wire-input-number__input::-webkit-inner-spin-button, .wire-input-number__input::-webkit-outer-spin-button { appearance: none; }
.wire-input-number__input::placeholder { color: var(--wire-color-muted); }
.wire-input-number__input:read-only { cursor: default; }
.wire-input-number__input:disabled { cursor: not-allowed; }
.wire-input-number:is([data-variant="horizontal"], [data-variant="compact"], [data-variant="seat"]) .wire-input-number__input { text-align: center; }
.wire-input-number[data-variant="seat"] .wire-input-number__input { width: 2.5rem; flex: none; }
.wire-input-number__buttons { display: flex; flex: none; border-left: 1px solid var(--wire-color-border); }
.wire-input-number[data-variant="vertical"] .wire-input-number__buttons { flex-direction: column; }
.wire-input-number__button { display: inline-flex; width: 2.5rem; flex: none; align-items: center; justify-content: center; padding: 0; border: 0; color: var(--wire-color-muted); background: transparent; transition: color var(--wire-motion-fast), background var(--wire-motion-fast); }
.wire-input-number[data-size="xs"] .wire-input-number__button { width: 2rem; } .wire-input-number[data-size="sm"] .wire-input-number__button { width: 2.25rem; } .wire-input-number[data-size="lg"] .wire-input-number__button { width: 3rem; } .wire-input-number[data-size="xl"] .wire-input-number__button { width: 3.5rem; }
.wire-input-number__button--start { border-right: 1px solid var(--wire-color-border); } .wire-input-number__button--end { border-left: 1px solid var(--wire-color-border); }
.wire-input-number__button--decrement { border-right: 1px solid var(--wire-color-border); }
.wire-input-number[data-variant="vertical"] .wire-input-number__button--decrement { border-right: 0; border-bottom: 1px solid var(--wire-color-border); }
.wire-input-number__button:hover { color: var(--input-number-accent); background: var(--wire-color-surface-2); }
.wire-input-number__button:focus-visible { z-index: 1; outline: 2px solid var(--input-number-accent); outline-offset: -2px; }
.wire-input-number__button:disabled { cursor: not-allowed; opacity: 0.4; }
.wire-input-number__icon { width: 1rem; height: 1rem; }
.wire-input-number__message--error { display: flex; align-items: center; gap: 0.375rem; color: var(--wire-color-danger); }
.wire-input-number__error-icon { width: 0.875rem; height: 0.875rem; flex: none; }
.wrn-input-number { position: relative; display: flex; width: 100%; max-width: 24rem; flex-direction: column; gap: 0.375rem; color: var(--wrn-color-text); }
.wrn-input-number[data-full-width="true"] { max-width: none; }
.wrn-input-number[data-variant="compact"] { width: fit-content; max-width: 100%; }
.wrn-input-number[data-disabled="true"] { opacity: 0.6; }
.wrn-input-number__label { display: inline-flex; align-items: center; gap: 0.25rem; color: var(--wrn-color-text); font-size: 0.875rem; font-weight: 600; line-height: 1.25rem; }
.wrn-input-number__required { margin-left: 0.125rem; color: var(--wrn-color-danger); }
.wrn-input-number__description, .wrn-input-number__message { margin: 0; color: var(--wrn-color-muted); font-size: 0.75rem; line-height: 1.25rem; }
.wrn-input-number__control { display: flex; min-width: 0; min-height: 2.5rem; overflow: hidden; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius-sm); color: var(--wrn-color-text); background: var(--wrn-color-surface); box-shadow: var(--wrn-shadow-1); transition: border-color var(--wrn-motion-base), box-shadow var(--wrn-motion-base), background var(--wrn-motion-base); }
.wrn-input-number__control:focus-within { border-color: var(--input-number-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--input-number-accent) 18%, transparent); }
.wrn-input-number[data-invalid="true"] .wrn-input-number__control { border-color: var(--wrn-color-danger); }
.wrn-input-number[data-invalid="true"] .wrn-input-number__control:focus-within { box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 18%, transparent); }
.wrn-input-number[data-disabled="true"] .wrn-input-number__control { cursor: not-allowed; background: var(--wrn-color-surface-2); }
.wrn-input-number[data-variant="compact"] .wrn-input-number__control { border-radius: 999px; }
.wrn-input-number[data-size="xs"] .wrn-input-number__control { min-height: 2rem; font-size: 0.75rem; }
.wrn-input-number[data-size="sm"] .wrn-input-number__control { min-height: 2.25rem; font-size: 0.875rem; }
.wrn-input-number[data-size="lg"] .wrn-input-number__control { min-height: 3rem; font-size: 1rem; }
.wrn-input-number[data-size="xl"] .wrn-input-number__control { min-height: 3.5rem; font-size: 1.125rem; }
.wrn-input-number__content { display: flex; min-width: 0; flex: 1; align-items: center; padding-inline: 0.75rem; }
.wrn-input-number[data-size="xs"] .wrn-input-number__content { padding-inline: 0.5rem; }
.wrn-input-number[data-size="sm"] .wrn-input-number__content { padding-inline: 0.625rem; }
.wrn-input-number[data-size="lg"] .wrn-input-number__content { padding-inline: 1rem; }
.wrn-input-number[data-size="xl"] .wrn-input-number__content { padding-inline: 1.25rem; }
.wrn-input-number[data-variant="labeled"] .wrn-input-number__content { flex-direction: column; align-items: stretch; justify-content: center; gap: 0.125rem; }
.wrn-input-number[data-variant="seat"] .wrn-input-number__content { justify-content: space-between; gap: 0.75rem; }
.wrn-input-number__copy { min-width: 0; flex: 1; }
.wrn-input-number__inner-label, .wrn-input-number__inner-description { display: block; overflow: hidden; color: var(--wrn-color-text); font-size: 0.875rem; font-weight: 600; line-height: 1rem; text-overflow: ellipsis; white-space: nowrap; }
.wrn-input-number__inner-description { color: var(--wrn-color-muted); font-size: 0.75rem; font-weight: 400; }
.wrn-input-number[data-variant="labeled"] .wrn-input-number__inner-label { color: var(--wrn-color-muted); font-size: 0.75rem; font-weight: 500; }
.wrn-input-number__field { display: flex; width: 100%; min-width: 0; align-items: center; }
.wrn-input-number[data-variant="seat"] .wrn-input-number__field { width: auto; flex: none; }
.wrn-input-number__prefix, .wrn-input-number__suffix { flex: none; color: var(--wrn-color-muted); }
.wrn-input-number__prefix { padding-right: 0.375rem; } .wrn-input-number__suffix { padding-left: 0.375rem; }
.wrn-input-number__input { width: 100%; min-width: 0; flex: 1; padding: 0; border: 0; outline: none; color: var(--wrn-color-text); background: transparent; font: inherit; font-weight: 500; line-height: 1; appearance: textfield; }
.wrn-input-number__input::-webkit-inner-spin-button, .wrn-input-number__input::-webkit-outer-spin-button { appearance: none; }
.wrn-input-number__input::placeholder { color: var(--wrn-color-muted); }
.wrn-input-number__input:read-only { cursor: default; }
.wrn-input-number__input:disabled { cursor: not-allowed; }
.wrn-input-number:is([data-variant="horizontal"], [data-variant="compact"], [data-variant="seat"]) .wrn-input-number__input { text-align: center; }
.wrn-input-number[data-variant="seat"] .wrn-input-number__input { width: 2.5rem; flex: none; }
.wrn-input-number__buttons { display: flex; flex: none; border-left: 1px solid var(--wrn-color-border); }
.wrn-input-number[data-variant="vertical"] .wrn-input-number__buttons { flex-direction: column; }
.wrn-input-number__button { display: inline-flex; width: 2.5rem; flex: none; align-items: center; justify-content: center; padding: 0; border: 0; color: var(--wrn-color-muted); background: transparent; transition: color var(--wrn-motion-fast), background var(--wrn-motion-fast); }
.wrn-input-number[data-size="xs"] .wrn-input-number__button { width: 2rem; } .wrn-input-number[data-size="sm"] .wrn-input-number__button { width: 2.25rem; } .wrn-input-number[data-size="lg"] .wrn-input-number__button { width: 3rem; } .wrn-input-number[data-size="xl"] .wrn-input-number__button { width: 3.5rem; }
.wrn-input-number__button--start { border-right: 1px solid var(--wrn-color-border); } .wrn-input-number__button--end { border-left: 1px solid var(--wrn-color-border); }
.wrn-input-number__button--decrement { border-right: 1px solid var(--wrn-color-border); }
.wrn-input-number[data-variant="vertical"] .wrn-input-number__button--decrement { border-right: 0; border-bottom: 1px solid var(--wrn-color-border); }
.wrn-input-number__button:hover { color: var(--input-number-accent); background: var(--wrn-color-surface-2); }
.wrn-input-number__button:focus-visible { z-index: 1; outline: 2px solid var(--input-number-accent); outline-offset: -2px; }
.wrn-input-number__button:disabled { cursor: not-allowed; opacity: 0.4; }
.wrn-input-number__icon { width: 1rem; height: 1rem; }
.wrn-input-number__message--error { display: flex; align-items: center; gap: 0.375rem; color: var(--wrn-color-danger); }
.wrn-input-number__error-icon { width: 0.875rem; height: 0.875rem; flex: none; }
}
}
+8 -8
View File
@@ -16,37 +16,37 @@ component Kbd {
}
view {
<kbd {...attrs} class='wire-kbd {class}' data-size='{size}' data-color='{color}'>
<kbd {...attrs} class='wrn-kbd {class}' data-size='{size}' data-color='{color}'>
<slot>{label}</slot>
</kbd>
}
style {
.wire-kbd {
.wrn-kbd {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.6rem;
padding: 0.15rem 0.4rem;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
/* The lower edge is what reads as a physical key. */
border-bottom-width: 2px;
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface-soft);
color: var(--wire-color-text);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface-soft);
color: var(--wrn-color-text);
font-family: inherit;
font-size: 0.78rem;
font-weight: 600;
line-height: 1;
}
.wire-kbd[data-size="sm"] {
.wrn-kbd[data-size="sm"] {
min-width: 1.4rem;
padding: 0.1rem 0.3rem;
font-size: 0.7rem;
}
.wire-kbd[data-size="lg"] {
.wrn-kbd[data-size="lg"] {
min-width: 2rem;
padding: 0.3rem 0.55rem;
font-size: 0.9rem;
+30 -30
View File
@@ -11,7 +11,7 @@
// held in the DOM as the --wrn-split custom property and these styles read it.
//
// This component previously declared resizeStart, resize and resizeEnd with no
// pointer handling whatsoever: a caller wired up @resize and received nothing,
// pointer handling whatsoever: a caller connected up @resize and received nothing,
// for ever, with no error.
//
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
@@ -86,7 +86,7 @@ component LayoutSplitter {
<div
{...attrs}
data-ui-component="LayoutSplitter"
class='wire-splitter {class}'
class='wrn-splitter {class}'
data-color='{color}'
data-orientation='{orientation}'
data-wrn-splitter='{isVertical() ? "vertical" : "horizontal"}'
@@ -95,12 +95,12 @@ component LayoutSplitter {
style='--wrn-split:{currentSize()}%;'
@wrnexus:splitter:resize='reportResize(event)'
>
<div class="wire-splitter__pane wire-splitter__pane--start">
<div class="wrn-splitter__pane wrn-splitter__pane--start">
<slot name="start"></slot>
</div>
<div
class="wire-splitter__handle"
class="wrn-splitter__handle"
data-wrn-splitter-handle="true"
role="separator"
tabindex="0"
@@ -110,10 +110,10 @@ component LayoutSplitter {
aria-valuemin='{lowerBound()}'
aria-valuemax='{upperBound()}'
>
<span class="wire-splitter__grip" aria-hidden="true"></span>
<span class="wrn-splitter__grip" aria-hidden="true"></span>
</div>
<div class="wire-splitter__pane wire-splitter__pane--end">
<div class="wrn-splitter__pane wrn-splitter__pane--end">
<slot name="end"></slot>
</div>
@@ -122,8 +122,8 @@ component LayoutSplitter {
}
style {
.wire-splitter {
--splitter-accent: var(--wire-color-primary);
.wrn-splitter {
--splitter-accent: var(--wrn-color-primary);
display: grid;
/* The first track follows the size the runtime resolves. */
grid-template-columns: var(--wrn-split, 50%) auto minmax(0, 1fr);
@@ -132,34 +132,34 @@ component LayoutSplitter {
min-width: 0;
}
.wire-splitter[data-color="secondary"] {
--splitter-accent: var(--wire-color-secondary);
.wrn-splitter[data-color="secondary"] {
--splitter-accent: var(--wrn-color-secondary);
}
.wire-splitter[data-color="success"] {
--splitter-accent: var(--wire-color-success);
.wrn-splitter[data-color="success"] {
--splitter-accent: var(--wrn-color-success);
}
.wire-splitter[data-color="danger"] {
--splitter-accent: var(--wire-color-danger);
.wrn-splitter[data-color="danger"] {
--splitter-accent: var(--wrn-color-danger);
}
.wire-splitter[data-color="info"] {
--splitter-accent: var(--wire-color-info);
.wrn-splitter[data-color="info"] {
--splitter-accent: var(--wrn-color-info);
}
.wire-splitter[data-orientation="vertical"] {
.wrn-splitter[data-orientation="vertical"] {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: var(--wrn-split, 50%) auto minmax(0, 1fr);
}
.wire-splitter__pane {
.wrn-splitter__pane {
min-width: 0;
min-height: 0;
overflow: auto;
}
.wire-splitter__handle {
.wrn-splitter__handle {
display: flex;
align-items: center;
justify-content: center;
@@ -173,36 +173,36 @@ component LayoutSplitter {
user-select: none;
}
.wire-splitter[data-orientation="vertical"] .wire-splitter__handle {
.wrn-splitter[data-orientation="vertical"] .wrn-splitter__handle {
padding: 0.25rem 0;
cursor: row-resize;
}
.wire-splitter__grip {
.wrn-splitter__grip {
display: block;
width: 2px;
height: 100%;
min-height: 1.5rem;
border-radius: 999px;
background: var(--wire-color-border);
background: var(--wrn-color-border);
transition: background 140ms ease;
}
.wire-splitter[data-orientation="vertical"] .wire-splitter__grip {
.wrn-splitter[data-orientation="vertical"] .wrn-splitter__grip {
width: 100%;
min-width: 1.5rem;
height: 2px;
}
.wire-splitter__handle:hover .wire-splitter__grip,
.wire-splitter[data-wrn-splitter-dragging="true"] .wire-splitter__grip {
.wrn-splitter__handle:hover .wrn-splitter__grip,
.wrn-splitter[data-wrn-splitter-dragging="true"] .wrn-splitter__grip {
background: var(--splitter-accent);
}
.wire-splitter__handle:focus-visible {
.wrn-splitter__handle:focus-visible {
outline: 2px solid var(--splitter-accent);
outline-offset: -2px;
border-radius: var(--wire-radius-sm);
border-radius: var(--wrn-radius-sm);
}
/*
@@ -211,13 +211,13 @@ component LayoutSplitter {
* no longer has a second track to trade against.
*/
@media (max-width: 639px) {
.wire-splitter,
.wire-splitter[data-orientation="vertical"] {
.wrn-splitter,
.wrn-splitter[data-orientation="vertical"] {
grid-template-columns: minmax(0, 1fr);
grid-template-rows: auto auto auto;
}
.wire-splitter__handle {
.wrn-splitter__handle {
cursor: default;
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component LegendIndicator {
outputs {
toggle(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
@@ -16,27 +14,95 @@ component LegendIndicator {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--legend-indicator wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--legend-indicator wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--legend-indicator {
.wrn-next--legend-indicator {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--legend-indicator > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--legend-indicator > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--legend-indicator > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--legend-indicator > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--legend-indicator > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--legend-indicator > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+35 -35
View File
@@ -25,7 +25,7 @@ component Link {
<a
{...attrs}
data-ui-component="Link"
class='wire-link {class}'
class='wrn-link {class}'
href='{href}'
target='{target}'
rel='{external ? (rel || "noopener noreferrer") : rel}'
@@ -34,9 +34,9 @@ component Link {
data-underline='{underline}'
data-external='{external}'
>
<span class="wire-link__label">{label}</span>
<span class="wrn-link__label">{label}</span>
<span
class="icon-[lucide--external-link] wire-link__icon"
class="icon-[lucide--external-link] wrn-link__icon"
data-show="external"
aria-hidden="true"
></span>
@@ -45,14 +45,14 @@ component Link {
}
style {
.wire-link {
--link-tone: var(--wire-color-primary);
--link-tone-hover: var(--wire-color-primary-hover);
.wrn-link {
--link-tone: var(--wrn-color-primary);
--link-tone-hover: var(--wrn-color-primary-hover);
display: inline-flex;
align-items: center;
gap: 0.35rem;
min-width: 0;
border-radius: var(--wire-radius-sm);
border-radius: var(--wrn-radius-sm);
color: var(--link-tone);
font-size: 0.875rem;
font-weight: 600;
@@ -61,74 +61,74 @@ component Link {
transition: color 140ms ease;
}
.wire-link[data-color="secondary"] {
--link-tone: var(--wire-color-secondary);
--link-tone-hover: var(--wire-color-secondary-hover);
.wrn-link[data-color="secondary"] {
--link-tone: var(--wrn-color-secondary);
--link-tone-hover: var(--wrn-color-secondary-hover);
}
.wire-link[data-color="success"] {
--link-tone: var(--wire-color-success);
--link-tone-hover: var(--wire-color-success);
.wrn-link[data-color="success"] {
--link-tone: var(--wrn-color-success);
--link-tone-hover: var(--wrn-color-success);
}
.wire-link[data-color="warning"] {
--link-tone: var(--wire-color-warning-text);
--link-tone-hover: var(--wire-color-warning-text);
.wrn-link[data-color="warning"] {
--link-tone: var(--wrn-color-warning-text);
--link-tone-hover: var(--wrn-color-warning-text);
}
.wire-link[data-color="danger"] {
--link-tone: var(--wire-color-danger);
--link-tone-hover: var(--wire-color-danger);
.wrn-link[data-color="danger"] {
--link-tone: var(--wrn-color-danger);
--link-tone-hover: var(--wrn-color-danger);
}
.wire-link[data-color="info"] {
--link-tone: var(--wire-color-info);
--link-tone-hover: var(--wire-color-info);
.wrn-link[data-color="info"] {
--link-tone: var(--wrn-color-info);
--link-tone-hover: var(--wrn-color-info);
}
.wire-link[data-color="neutral"] {
--link-tone: var(--wire-color-text);
--link-tone-hover: var(--wire-color-text);
.wrn-link[data-color="neutral"] {
--link-tone: var(--wrn-color-text);
--link-tone-hover: var(--wrn-color-text);
}
.wire-link[data-size="xs"] {
.wrn-link[data-size="xs"] {
font-size: 0.75rem;
}
.wire-link[data-size="md"] {
.wrn-link[data-size="md"] {
font-size: 1rem;
}
.wire-link[data-size="lg"] {
.wrn-link[data-size="lg"] {
font-size: 1.125rem;
}
.wire-link[data-underline="always"] {
.wrn-link[data-underline="always"] {
text-decoration: underline;
}
.wire-link[data-underline="hover"]:hover {
.wrn-link[data-underline="hover"]:hover {
text-decoration: underline;
}
.wire-link:hover {
.wrn-link:hover {
color: var(--link-tone-hover);
}
.wire-link:focus-visible {
outline: 2px solid var(--wire-color-focus);
.wrn-link:focus-visible {
outline: 2px solid var(--wrn-color-focus);
outline-offset: 2px;
}
/* The label truncates; the outbound icon must not shrink with it. */
.wire-link__label {
.wrn-link__label {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.wire-link__icon {
.wrn-link__icon {
flex: 0 0 auto;
width: 0.875rem;
height: 0.875rem;
+49 -49
View File
@@ -17,76 +17,76 @@ component List {
<section
data-ui-component="List"
aria-label='{title}'
class='wire-list {class}'
class='wrn-list {class}'
>
{#if title || description}
<header class="wire-list__header">
<header class="wrn-list__header">
{#if title}
<h3 class="wire-list__title">{title}</h3>
<h3 class="wrn-list__title">{title}</h3>
{/if}
{#if description}
<p class="wire-list__description">{description}</p>
<p class="wrn-list__description">{description}</p>
{/if}
</header>
{/if}
<ul
class="wire-list__items"
class="wrn-list__items"
data-variant='{variant}'
>
{#each items as item, index}
<li
class="wire-list__item"
class="wrn-list__item"
data-variant='{variant}'
@click='if (!item.href) { output.select({ item: item, index: index }) }'
>
{#if item.href}
<a
href='{item.href}'
class="wire-list__row"
class="wrn-list__row"
data-size='{size}'
@click='output.select({ item: item, index: index })'
>
{#if item.icon}
<span class="wire-list__leading-icon"><span class='{item.icon}' aria-hidden="true"></span>
<span class="wrn-list__leading-icon"><span class='{item.icon}' aria-hidden="true"></span>
</span>
{/if}
{#if item.imageSrc}
<img src='{item.imageSrc}' alt='{item.imageAlt || ""}' class="wire-list__image" loading="lazy" />
<img src='{item.imageSrc}' alt='{item.imageAlt || ""}' class="wrn-list__image" loading="lazy" />
{/if}
<div class="wire-list__body">
<div class="wire-list__heading">
<p class="wire-list__item-title">{item.title || item.label}</p>
<div class="wrn-list__body">
<div class="wrn-list__heading">
<p class="wrn-list__item-title">{item.title || item.label}</p>
{#if item.meta}
<span class="wire-list__meta">{item.meta}</span>
<span class="wrn-list__meta">{item.meta}</span>
{/if}
</div>
{#if item.description}
<p class="wire-list__item-description">{item.description}</p>
<p class="wrn-list__item-description">{item.description}</p>
{/if}
{#if item.badge}
<span class="wire-list__badge">{item.badge}</span>
<span class="wrn-list__badge">{item.badge}</span>
{/if}
</div>
<span class="icon-[lucide--chevron-right] wire-list__chevron" aria-hidden="true"></span>
<span class="icon-[lucide--chevron-right] wrn-list__chevron" aria-hidden="true"></span>
</a>
{:else}
<div class="wire-list__row" data-size='{size}'>
<div class="wrn-list__row" data-size='{size}'>
{#if item.icon}
<span class="wire-list__leading-icon"><span class='{item.icon}' aria-hidden="true"></span>
<span class="wrn-list__leading-icon"><span class='{item.icon}' aria-hidden="true"></span>
</span>
{/if}
<div class="wire-list__body">
<p class="wire-list__item-title">{item.title || item.label}</p>
<div class="wrn-list__body">
<p class="wrn-list__item-title">{item.title || item.label}</p>
{#if item.description}
<p class="wire-list__item-description">{item.description}</p>
<p class="wrn-list__item-description">{item.description}</p>
{/if}
</div>
</div>
{/if}
</li>
{:empty}
<li class="wire-list__empty">
<li class="wrn-list__empty">
No items available.
</li>
{/each}
@@ -97,32 +97,32 @@ component List {
}
style {
.wire-list { width: 100%; }
.wire-list__header { margin-bottom: 1rem; }
.wire-list__title { color: var(--wire-color-text); font-size: 1.125rem; font-weight: 700; }
.wire-list__description { margin-top: 0.25rem; color: var(--wire-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
.wire-list__items { overflow: hidden; }
.wire-list__items:is([data-variant="default"], [data-variant="divided"]) > .wire-list__item + .wire-list__item { border-top: 1px solid var(--wire-color-border); }
.wire-list__items[data-variant="card"] { border: 1px solid var(--wire-color-border); border-radius: calc(var(--wire-radius) * 1.5); background: var(--wire-color-surface-raised); }
.wire-list__items[data-variant="separated"] { display: grid; gap: 0.75rem; }
.wire-list__item { min-width: 0; }
.wire-list__item[data-variant="separated"] { border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); background: var(--wire-color-surface-raised); }
.wire-list__row { display: flex; min-width: 0; align-items: flex-start; gap: 0.75rem; padding: 0.75rem 1rem; outline: none; transition: background var(--wire-motion-base); }
a.wire-list__row:hover { background: var(--wire-color-surface-soft); }
.wire-list__row:focus-visible { box-shadow: inset 0 0 0 2px var(--wire-color-focus); }
.wire-list__row[data-size="sm"] { padding: 0.625rem 0.75rem; }
.wire-list__row[data-size="lg"] { padding: 1rem 1.25rem; }
.wire-list__leading-icon { display: flex; width: 2.5rem; height: 2.5rem; flex: none; align-items: center; justify-content: center; border-radius: var(--wire-radius); color: var(--wire-color-primary); background: var(--wire-color-primary-soft); }
.wire-list__leading-icon > span { width: 1.25rem; height: 1.25rem; }
.wire-list__image { width: 3rem; height: 3rem; flex: none; border-radius: var(--wire-radius); object-fit: cover; }
.wire-list__body { min-width: 0; flex: 1; }
.wire-list__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.wire-list__item-title { overflow: hidden; color: var(--wire-color-text); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.wire-list__meta { flex: none; color: var(--wire-color-text-muted); font-size: 0.75rem; }
.wire-list__item-description { display: -webkit-box; margin-top: 0.25rem; overflow: hidden; color: var(--wire-color-text-muted); font-size: 0.875rem; line-height: 1.25rem; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.wire-list__badge { display: inline-flex; margin-top: 0.5rem; padding: 0.25rem 0.625rem; border-radius: 999px; color: var(--wire-color-primary); background: var(--wire-color-primary-soft); font-size: 0.75rem; font-weight: 600; }
.wire-list__chevron { width: 1rem; height: 1rem; flex: none; margin-top: 0.25rem; color: var(--wire-color-text-muted); transition: color var(--wire-motion-base), transform var(--wire-motion-base); }
.wire-list__row:hover .wire-list__chevron { color: var(--wire-color-primary); transform: translateX(0.125rem); }
.wire-list__empty { padding: 1.5rem; border: 1px dashed var(--wire-color-border); border-radius: var(--wire-radius); color: var(--wire-color-text-muted); font-size: 0.875rem; text-align: center; }
.wrn-list { width: 100%; }
.wrn-list__header { margin-bottom: 1rem; }
.wrn-list__title { color: var(--wrn-color-text); font-size: 1.125rem; font-weight: 700; }
.wrn-list__description { margin-top: 0.25rem; color: var(--wrn-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
.wrn-list__items { overflow: hidden; }
.wrn-list__items:is([data-variant="default"], [data-variant="divided"]) > .wrn-list__item + .wrn-list__item { border-top: 1px solid var(--wrn-color-border); }
.wrn-list__items[data-variant="card"] { border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); }
.wrn-list__items[data-variant="separated"] { display: grid; gap: 0.75rem; }
.wrn-list__item { min-width: 0; }
.wrn-list__item[data-variant="separated"] { border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius); background: var(--wrn-color-surface-raised); }
.wrn-list__row { display: flex; min-width: 0; align-items: flex-start; gap: 0.75rem; padding: 0.75rem 1rem; outline: none; transition: background var(--wrn-motion-base); }
a.wrn-list__row:hover { background: var(--wrn-color-surface-soft); }
.wrn-list__row:focus-visible { box-shadow: inset 0 0 0 2px var(--wrn-color-focus); }
.wrn-list__row[data-size="sm"] { padding: 0.625rem 0.75rem; }
.wrn-list__row[data-size="lg"] { padding: 1rem 1.25rem; }
.wrn-list__leading-icon { display: flex; width: 2.5rem; height: 2.5rem; flex: none; align-items: center; justify-content: center; border-radius: var(--wrn-radius); color: var(--wrn-color-primary); background: var(--wrn-color-primary-soft); }
.wrn-list__leading-icon > span { width: 1.25rem; height: 1.25rem; }
.wrn-list__image { width: 3rem; height: 3rem; flex: none; border-radius: var(--wrn-radius); object-fit: cover; }
.wrn-list__body { min-width: 0; flex: 1; }
.wrn-list__heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.wrn-list__item-title { overflow: hidden; color: var(--wrn-color-text); font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.wrn-list__meta { flex: none; color: var(--wrn-color-text-muted); font-size: 0.75rem; }
.wrn-list__item-description { display: -webkit-box; margin-top: 0.25rem; overflow: hidden; color: var(--wrn-color-text-muted); font-size: 0.875rem; line-height: 1.25rem; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
.wrn-list__badge { display: inline-flex; margin-top: 0.5rem; padding: 0.25rem 0.625rem; border-radius: 999px; color: var(--wrn-color-primary); background: var(--wrn-color-primary-soft); font-size: 0.75rem; font-weight: 600; }
.wrn-list__chevron { width: 1rem; height: 1rem; flex: none; margin-top: 0.25rem; color: var(--wrn-color-text-muted); transition: color var(--wrn-motion-base), transform var(--wrn-motion-base); }
.wrn-list__row:hover .wrn-list__chevron { color: var(--wrn-color-primary); transform: translateX(0.125rem); }
.wrn-list__empty { padding: 1.5rem; border: 1px dashed var(--wrn-color-border); border-radius: var(--wrn-radius); color: var(--wrn-color-text-muted); font-size: 0.875rem; text-align: center; }
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component ListGroup {
outputs {
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)
@@ -16,27 +14,95 @@ component ListGroup {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--list-group wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--list-group wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--list-group {
.wrn-next--list-group {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--list-group > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--list-group > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--list-group > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--list-group > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--list-group > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--list-group > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+33 -33
View File
@@ -21,34 +21,34 @@ component Map {
<section
data-ui-component="Map"
aria-label='{title}'
class='wire-map {class}'
class='wrn-map {class}'
>
{#if title || description}
<header class="wire-map__header">
<header class="wrn-map__header">
<div>
{#if title}
<h3 class="wire-map__title">{title}</h3>
<h3 class="wrn-map__title">{title}</h3>
{/if}
{#if description}
<p class="wire-map__description">{description}</p>
<p class="wrn-map__description">{description}</p>
{/if}
</div>
<span class="wire-map__header-icon"><span class="icon-[lucide--map]" aria-hidden="true"></span>
<span class="wrn-map__header-icon"><span class="icon-[lucide--map]" aria-hidden="true"></span>
</span>
</header>
{/if}
<div
class="wire-map__viewport"
class="wrn-map__viewport"
data-size='{size}'
>
<div
class="wire-map__grid"
style="background-image: linear-gradient(var(--wire-color-border) 1px, transparent 1px), linear-gradient(90deg, var(--wire-color-border) 1px, transparent 1px); background-size: 32px 32px;"
class="wrn-map__grid"
style="background-image: linear-gradient(var(--wrn-color-border) 1px, transparent 1px), linear-gradient(90deg, var(--wrn-color-border) 1px, transparent 1px); background-size: 32px 32px;"
aria-hidden="true"
></div>
<div class="wire-map__content">
<div class="wrn-map__content">
<slot></slot>
</div>
@@ -57,7 +57,7 @@ component Map {
<button
type="button"
aria-label='{item.label || item.title || "Map marker"}'
class="wire-map__marker"
class="wrn-map__marker"
style='left: {item.x || (20 + index * 12)}%; top: {item.y || (30 + (index % 3) * 18)}%;'
@click='output.markerClick(item); output.select(item)'
>
@@ -66,22 +66,22 @@ component Map {
{/each}
{/if}
<div class="wire-map__controls">
<div class="wrn-map__controls">
<button
type="button"
aria-label="Zoom in"
class="wire-map__control"
class="wrn-map__control"
@click='output.zoom({ direction: "in" })'
>
<span class="icon-[lucide--plus] wire-map__control-icon" aria-hidden="true"></span>
<span class="icon-[lucide--plus] wrn-map__control-icon" aria-hidden="true"></span>
</button>
<button
type="button"
aria-label="Zoom out"
class="wire-map__control"
class="wrn-map__control"
@click='output.zoom({ direction: "out" })'
>
<span class="icon-[lucide--minus] wire-map__control-icon" aria-hidden="true"></span>
<span class="icon-[lucide--minus] wrn-map__control-icon" aria-hidden="true"></span>
</button>
</div>
</div>
@@ -89,23 +89,23 @@ component Map {
}
style {
.wire-map { overflow: hidden; border: 1px solid var(--wire-color-border); border-radius: calc(var(--wire-radius) * 1.5); background: var(--wire-color-surface-raised); box-shadow: var(--wire-shadow-1); }
.wire-map__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.25rem; border-bottom: 1px solid var(--wire-color-border); }
.wire-map__title { color: var(--wire-color-text); font-size: 1.125rem; font-weight: 700; }
.wire-map__description { margin-top: 0.25rem; color: var(--wire-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
.wire-map__header-icon { display: flex; width: 2.5rem; height: 2.5rem; flex: none; align-items: center; justify-content: center; border-radius: var(--wire-radius); color: var(--wire-color-primary); background: var(--wire-color-primary-soft); }
.wire-map__header-icon > span { width: 1.25rem; height: 1.25rem; }
.wire-map__viewport { position: relative; isolation: isolate; height: 20rem; overflow: hidden; background: var(--wire-color-surface-soft); }
.wire-map__viewport[data-size="sm"] { height: 16rem; }
.wire-map__viewport[data-size="lg"] { height: 28rem; }
.wire-map__grid { position: absolute; inset: 0; opacity: 0.5; pointer-events: none; }
.wire-map__content { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 1.5rem; }
.wire-map__marker { position: absolute; display: inline-flex; width: 2.5rem; height: 2.5rem; align-items: center; justify-content: center; padding: 0; border: 4px solid var(--wire-color-surface-raised); border-radius: 50%; color: var(--wire-color-on-primary); background: var(--wire-color-primary); box-shadow: var(--wire-shadow-3); transition: transform var(--wire-motion-base) var(--wire-ease-standard); }
.wire-map__marker:hover { transform: scale(1.1); }
.wire-map__controls { position: absolute; right: 1rem; bottom: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
.wire-map__control { display: inline-flex; width: 2.5rem; height: 2.5rem; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); color: var(--wire-color-text); background: var(--wire-color-surface-raised); box-shadow: var(--wire-shadow-1); transition: background var(--wire-motion-base); }
.wire-map__control:hover { background: var(--wire-color-surface-soft); }
.wire-map__marker:focus-visible, .wire-map__control:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: 2px; }
.wire-map__control-icon { width: 1rem; height: 1rem; }
.wrn-map { overflow: hidden; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); }
.wrn-map__header { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; padding: 1.25rem; border-bottom: 1px solid var(--wrn-color-border); }
.wrn-map__title { color: var(--wrn-color-text); font-size: 1.125rem; font-weight: 700; }
.wrn-map__description { margin-top: 0.25rem; color: var(--wrn-color-text-muted); font-size: 0.875rem; line-height: 1.5rem; }
.wrn-map__header-icon { display: flex; width: 2.5rem; height: 2.5rem; flex: none; align-items: center; justify-content: center; border-radius: var(--wrn-radius); color: var(--wrn-color-primary); background: var(--wrn-color-primary-soft); }
.wrn-map__header-icon > span { width: 1.25rem; height: 1.25rem; }
.wrn-map__viewport { position: relative; isolation: isolate; height: 20rem; overflow: hidden; background: var(--wrn-color-surface-soft); }
.wrn-map__viewport[data-size="sm"] { height: 16rem; }
.wrn-map__viewport[data-size="lg"] { height: 28rem; }
.wrn-map__grid { position: absolute; inset: 0; opacity: 0.5; pointer-events: none; }
.wrn-map__content { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 1.5rem; }
.wrn-map__marker { position: absolute; display: inline-flex; width: 2.5rem; height: 2.5rem; align-items: center; justify-content: center; padding: 0; border: 4px solid var(--wrn-color-surface-raised); border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); box-shadow: var(--wrn-shadow-3); transition: transform var(--wrn-motion-base) var(--wrn-ease-standard); }
.wrn-map__marker:hover { transform: scale(1.1); }
.wrn-map__controls { position: absolute; right: 1rem; bottom: 1rem; display: flex; flex-direction: column; gap: 0.5rem; }
.wrn-map__control { display: inline-flex; width: 2.5rem; height: 2.5rem; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--wrn-color-border); border-radius: var(--wrn-radius); color: var(--wrn-color-text); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: background var(--wrn-motion-base); }
.wrn-map__control:hover { background: var(--wrn-color-surface-soft); }
.wrn-map__marker:focus-visible, .wrn-map__control:focus-visible { outline: 2px solid var(--wrn-color-focus); outline-offset: 2px; }
.wrn-map__control-icon { width: 1rem; height: 1rem; }
}
}
+39 -39
View File
@@ -21,44 +21,44 @@ component Marquee {
<section
data-ui-component="Marquee"
aria-label='{title}'
class='wire-marquee {class}'
class='wrn-marquee {class}'
>
<div class="wire-marquee__layout">
<div class="wrn-marquee__layout">
{#if title}
<div class="wire-marquee__title">
<span class="icon-[lucide--megaphone] wire-marquee__icon" aria-hidden="true"></span>
<div class="wrn-marquee__title">
<span class="icon-[lucide--megaphone] wrn-marquee__icon" aria-hidden="true"></span>
<span>{title}</span>
</div>
{/if}
<div
class="wire-marquee__window"
class="wrn-marquee__window"
@mouseenter='paused = true; output.pause({})'
@mouseleave='paused = false; output.resume({})'
@focusin='paused = true'
@focusout='paused = false'
>
<div
class="wire-marquee__track"
class:wire-marquee-paused='paused'
class="wrn-marquee__track"
class:wrn-marquee-paused='paused'
>
{#each items as item}
<a
href='{item.href || "#"}'
class="wire-marquee__item"
class="wrn-marquee__item"
>
<span
class="wire-marquee__dot"
class="wrn-marquee__dot"
data-color='{item.color || "primary"}'
aria-hidden="true"
></span>
<span>{item.label || item.title}</span>
{#if item.meta}
<span class="wire-marquee__meta">{item.meta}</span>
<span class="wrn-marquee__meta">{item.meta}</span>
{/if}
</a>
{:empty}
<p class="wire-marquee__empty">{description || "No announcements available."}</p>
<p class="wrn-marquee__empty">{description || "No announcements available."}</p>
{/each}
{#if items.length > 0}
@@ -67,9 +67,9 @@ component Marquee {
href='{item.href || "#"}'
aria-hidden="true"
tabindex="-1"
class="wire-marquee__item"
class="wrn-marquee__item"
>
<span class="wire-marquee__dot" aria-hidden="true"></span>
<span class="wrn-marquee__dot" aria-hidden="true"></span>
<span>{item.label || item.title}</span>
</a>
{/each}
@@ -80,11 +80,11 @@ component Marquee {
<button
type="button"
aria-label='{paused ? "Resume announcements" : "Pause announcements"}'
class="wire-marquee__toggle"
class="wrn-marquee__toggle"
@click='paused = !paused; paused ? output.pause({}) : output.resume({})'
>
<span class="icon-[lucide--pause] wire-marquee__icon" data-show='!paused' aria-hidden="true"></span>
<span class="icon-[lucide--play] wire-marquee__icon" data-show='paused' aria-hidden="true"></span>
<span class="icon-[lucide--pause] wrn-marquee__icon" data-show='!paused' aria-hidden="true"></span>
<span class="icon-[lucide--play] wrn-marquee__icon" data-show='paused' aria-hidden="true"></span>
</button>
</div>
@@ -93,40 +93,40 @@ component Marquee {
}
style {
.wire-marquee { overflow: hidden; border-block: 1px solid var(--wire-color-border); background: var(--wire-color-surface-raised); }
.wire-marquee__layout { display: flex; align-items: stretch; }
.wire-marquee__title { position: relative; z-index: 1; display: flex; flex: none; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; border-right: 1px solid var(--wire-color-border); color: var(--wire-color-on-primary); background: var(--wire-color-primary); font-size: 0.875rem; font-weight: 700; }
.wire-marquee__window { position: relative; min-width: 0; flex: 1; overflow: hidden; }
.wire-marquee__track {
.wrn-marquee { overflow: hidden; border-block: 1px solid var(--wrn-color-border); background: var(--wrn-color-surface-raised); }
.wrn-marquee__layout { display: flex; align-items: stretch; }
.wrn-marquee__title { position: relative; z-index: 1; display: flex; flex: none; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem; border-right: 1px solid var(--wrn-color-border); color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); font-size: 0.875rem; font-weight: 700; }
.wrn-marquee__window { position: relative; min-width: 0; flex: 1; overflow: hidden; }
.wrn-marquee__track {
display: flex; width: max-content; min-width: 100%; align-items: center;
animation: wire-marquee 32s linear infinite;
animation: wrn-marquee 32s linear infinite;
}
.wire-marquee-paused { animation-play-state: paused; }
.wire-marquee__item { display: flex; flex: none; align-items: center; gap: 0.75rem; padding: 0.75rem 1.25rem; color: var(--wire-color-text); font-size: 0.875rem; font-weight: 500; transition: color var(--wire-motion-base), background var(--wire-motion-base); }
.wire-marquee__item:hover { color: var(--wire-color-primary); background: var(--wire-color-primary-soft); }
.wire-marquee__item:focus-visible, .wire-marquee__toggle:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: -2px; }
.wire-marquee__dot { width: 0.5rem; height: 0.5rem; flex: none; border-radius: 50%; background: var(--wire-color-primary); }
.wire-marquee__dot[data-color="danger"] { background: var(--wire-color-danger); }
.wire-marquee__dot[data-color="warning"] { background: var(--wire-color-warning); }
.wire-marquee__dot[data-color="success"] { background: var(--wire-color-success); }
.wire-marquee__meta { color: var(--wire-color-text-muted); font-size: 0.75rem; }
.wire-marquee__empty { padding: 0.75rem 1.25rem; color: var(--wire-color-text-muted); font-size: 0.875rem; }
.wire-marquee__toggle { display: flex; flex: none; align-items: center; justify-content: center; padding-inline: 1rem; border: 0; border-left: 1px solid var(--wire-color-border); color: var(--wire-color-text-muted); background: transparent; transition: color var(--wire-motion-base), background var(--wire-motion-base); }
.wire-marquee__toggle:hover { color: var(--wire-color-text); background: var(--wire-color-surface-soft); }
.wire-marquee__icon { width: 1rem; height: 1rem; }
@media (min-width: 40rem) { .wire-marquee__title { padding-inline: 1.25rem; } }
.wrn-marquee-paused { animation-play-state: paused; }
.wrn-marquee__item { display: flex; flex: none; align-items: center; gap: 0.75rem; padding: 0.75rem 1.25rem; color: var(--wrn-color-text); font-size: 0.875rem; font-weight: 500; transition: color var(--wrn-motion-base), background var(--wrn-motion-base); }
.wrn-marquee__item:hover { color: var(--wrn-color-primary); background: var(--wrn-color-primary-soft); }
.wrn-marquee__item:focus-visible, .wrn-marquee__toggle:focus-visible { outline: 2px solid var(--wrn-color-focus); outline-offset: -2px; }
.wrn-marquee__dot { width: 0.5rem; height: 0.5rem; flex: none; border-radius: 50%; background: var(--wrn-color-primary); }
.wrn-marquee__dot[data-color="danger"] { background: var(--wrn-color-danger); }
.wrn-marquee__dot[data-color="warning"] { background: var(--wrn-color-warning); }
.wrn-marquee__dot[data-color="success"] { background: var(--wrn-color-success); }
.wrn-marquee__meta { color: var(--wrn-color-text-muted); font-size: 0.75rem; }
.wrn-marquee__empty { padding: 0.75rem 1.25rem; color: var(--wrn-color-text-muted); font-size: 0.875rem; }
.wrn-marquee__toggle { display: flex; flex: none; align-items: center; justify-content: center; padding-inline: 1rem; border: 0; border-left: 1px solid var(--wrn-color-border); color: var(--wrn-color-text-muted); background: transparent; transition: color var(--wrn-motion-base), background var(--wrn-motion-base); }
.wrn-marquee__toggle:hover { color: var(--wrn-color-text); background: var(--wrn-color-surface-soft); }
.wrn-marquee__icon { width: 1rem; height: 1rem; }
@media (min-width: 40rem) { .wrn-marquee__title { padding-inline: 1.25rem; } }
.wire-marquee-paused {
.wrn-marquee-paused {
animation-play-state: paused;
}
@keyframes wire-marquee {
@keyframes wrn-marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
.wire-marquee__track {
.wrn-marquee__track {
animation: none;
}
}
+66 -66
View File
@@ -80,7 +80,7 @@ component MegaMenu {
<div
{...attrs}
data-ui-component="MegaMenu"
class='wire-mega {class}'
class='wrn-mega {class}'
data-color='{color}'
data-size='{size}'
data-open='{visible}'
@@ -89,46 +89,46 @@ component MegaMenu {
>
<button
type="button"
class="wire-mega__trigger"
class="wrn-mega__trigger"
aria-haspopup="true"
aria-expanded='{visible}'
@click='togglePanel(event)'
@mouseenter='showPanel(event)'
@focus='showPanel(event)'
>
<span class='wire-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span>
<span class="wire-mega__trigger-label">{label}</span>
<span class="wire-mega__arrow" aria-hidden="true">&#8250;</span>
<span class='wrn-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span>
<span class="wrn-mega__trigger-label">{label}</span>
<span class="wrn-mega__arrow" aria-hidden="true">&#8250;</span>
</button>
<div
class="wire-mega__panel"
class="wrn-mega__panel"
data-wrn-anchored="true"
data-show="visible"
data-wrn-roving="both"
>
<div class="wire-mega__columns">
<div class="wrn-mega__columns">
{#each columnList() as column}
<section class="wire-mega__column">
<h3 class="wire-mega__heading" data-show="column.heading">{column.heading}</h3>
<ul class="wire-mega__list">
<section class="wrn-mega__column">
<h3 class="wrn-mega__heading" data-show="column.heading">{column.heading}</h3>
<ul class="wrn-mega__list">
{#each itemsOf(column) as item}
<li>
<a
class="wire-mega__link"
class="wrn-mega__link"
href='{item.href || "#"}'
data-wrn-roving-item="true"
aria-disabled='{item.disabled ? "true" : "false"}'
@click='choose(item)'
>
<span
class='wire-mega__link-icon {item.icon}'
class='wrn-mega__link-icon {item.icon}'
data-show="item.icon"
aria-hidden="true"
></span>
<span class="wire-mega__link-body">
<span class="wire-mega__link-label">{item.label}</span>
<span class="wire-mega__link-description" data-show="item.description">
<span class="wrn-mega__link-body">
<span class="wrn-mega__link-label">{item.label}</span>
<span class="wrn-mega__link-description" data-show="item.description">
{item.description}
</span>
</span>
@@ -140,77 +140,77 @@ component MegaMenu {
{/each}
</div>
<p class="wire-mega__footer" data-show="footer">{footer}</p>
<p class="wrn-mega__footer" data-show="footer">{footer}</p>
<slot />
</div>
</div>
}
style {
.wire-mega {
--mega-accent: var(--wire-color-primary);
.wrn-mega {
--mega-accent: var(--wrn-color-primary);
position: relative;
display: inline-block;
max-width: 100%;
}
.wire-mega[data-color="secondary"] {
--mega-accent: var(--wire-color-secondary);
.wrn-mega[data-color="secondary"] {
--mega-accent: var(--wrn-color-secondary);
}
.wire-mega[data-color="success"] {
--mega-accent: var(--wire-color-success);
.wrn-mega[data-color="success"] {
--mega-accent: var(--wrn-color-success);
}
.wire-mega[data-color="danger"] {
--mega-accent: var(--wire-color-danger);
.wrn-mega[data-color="danger"] {
--mega-accent: var(--wrn-color-danger);
}
.wire-mega[data-color="info"] {
--mega-accent: var(--wire-color-info);
.wrn-mega[data-color="info"] {
--mega-accent: var(--wrn-color-info);
}
.wire-mega[data-size="sm"] {
.wrn-mega[data-size="sm"] {
font-size: 0.82rem;
}
.wire-mega[data-size="lg"] {
.wrn-mega[data-size="lg"] {
font-size: 1rem;
}
.wire-mega__trigger {
.wrn-mega__trigger {
appearance: none;
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.45rem 0.7rem;
border: 1px solid transparent;
border-radius: var(--wire-radius-sm);
border-radius: var(--wrn-radius-sm);
background: transparent;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font: inherit;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
}
.wire-mega__trigger:hover,
.wire-mega[data-open="true"] .wire-mega__trigger {
background: var(--wire-color-surface-soft);
color: var(--wire-color-text);
.wrn-mega__trigger:hover,
.wrn-mega[data-open="true"] .wrn-mega__trigger {
background: var(--wrn-color-surface-soft);
color: var(--wrn-color-text);
}
.wire-mega__trigger:focus-visible {
.wrn-mega__trigger:focus-visible {
outline: 2px solid var(--mega-accent);
outline-offset: 2px;
}
.wire-mega__arrow {
.wrn-mega__arrow {
display: inline-block;
transition: transform 160ms ease;
}
.wire-mega[data-open="true"] .wire-mega__arrow {
.wrn-mega[data-open="true"] .wrn-mega__arrow {
transform: rotate(90deg);
}
@@ -220,7 +220,7 @@ component MegaMenu {
* element, so crossing it fired mouseleave on the root. The bridge below
* covers the gap with a descendant, so the pointer never actually leaves.
*/
.wire-mega__panel::before {
.wrn-mega__panel::before {
content: "";
position: absolute;
left: 0;
@@ -229,7 +229,7 @@ component MegaMenu {
height: 0.6rem;
}
.wire-mega__panel {
.wrn-mega__panel {
position: absolute;
z-index: 40;
top: calc(100% + 0.4rem);
@@ -237,32 +237,32 @@ component MegaMenu {
width: max-content;
max-width: min(60rem, calc(100vw - 2rem));
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-lg);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-lg);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
}
.wire-mega__columns {
.wrn-mega__columns {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
gap: 1.25rem;
}
.wire-mega__column {
.wrn-mega__column {
min-width: 0;
}
.wire-mega__heading {
.wrn-mega__heading {
margin: 0 0 0.5rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.wire-mega__list {
.wrn-mega__list {
display: grid;
gap: 0.15rem;
margin: 0;
@@ -270,52 +270,52 @@ component MegaMenu {
list-style: none;
}
.wire-mega__link {
.wrn-mega__link {
display: flex;
align-items: flex-start;
gap: 0.5rem;
padding: 0.45rem 0.5rem;
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text);
border-radius: var(--wrn-radius-sm);
color: var(--wrn-color-text);
text-decoration: none;
}
.wire-mega__link:hover {
background: var(--wire-color-surface-soft);
.wrn-mega__link:hover {
background: var(--wrn-color-surface-soft);
}
.wire-mega__link:focus-visible {
.wrn-mega__link:focus-visible {
outline: 2px solid var(--mega-accent);
outline-offset: 2px;
}
.wire-mega__link[aria-disabled="true"] {
.wrn-mega__link[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
}
.wire-mega__link-body {
.wrn-mega__link-body {
display: flex;
flex-direction: column;
min-width: 0;
}
.wire-mega__link-label {
.wrn-mega__link-label {
font-size: 0.88rem;
font-weight: 600;
}
.wire-mega__link-description {
color: var(--wire-color-text-muted);
.wrn-mega__link-description {
color: var(--wrn-color-text-muted);
font-size: 0.78rem;
line-height: 1.5;
}
.wire-mega__footer {
.wrn-mega__footer {
margin: 1rem 0 0;
padding-top: 0.75rem;
border-top: 1px solid var(--wire-color-border);
color: var(--wire-color-text-muted);
border-top: 1px solid var(--wrn-color-border);
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
}
@@ -325,18 +325,18 @@ component MegaMenu {
* a 60rem grid has nowhere to go on a 360px screen.
*/
@media (max-width: 767px) {
.wire-mega {
.wrn-mega {
display: block;
}
.wire-mega__panel {
.wrn-mega__panel {
position: static;
width: auto;
max-width: 100%;
box-shadow: none;
}
.wire-mega__columns {
.wrn-mega__columns {
grid-template-columns: 1fr;
}
}
+132 -132
View File
@@ -59,7 +59,7 @@ component Modal {
// )
//
// 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
// modal the dispatching element is actually inside -- no ids to wrn up
// and no way to close somebody else's modal by accident.
client function hideModal(reason, sourceEvent) {
visible = false
@@ -106,13 +106,13 @@ component Modal {
data-scrollable='{scrollable ? "true" : "false"}'
data-scroll='{scrollBehavior}'
data-destructive='{destructive ? "true" : "false"}'
class='wire-modal {class}'
class='wrn-modal {class}'
@wrnexus:modal:close='hideModal("api", event)'
>
{#if triggerLabel}
<button
type="button"
class="wire-modal__trigger"
class="wrn-modal__trigger"
aria-haspopup="dialog"
aria-expanded='{isOpen() ? "true" : "false"}'
@click='showModal(event)'
@@ -129,7 +129,7 @@ component Modal {
{/if}
<span
class="wire-modal__trigger-slot"
class="wrn-modal__trigger-slot"
@click='showModal(event)'
>
<slot
@@ -139,42 +139,42 @@ component Modal {
</span>
<div
class="wire-modal__layer"
class="wrn-modal__layer"
data-show="isOpen()"
role="presentation"
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-modal__backdrop"
class="wrn-modal__backdrop"
aria-label='{closeLabel}'
@click='if (closeOnBackdrop) { cancelModal(event) }'
>
</button>
<section
class="wire-modal__panel"
class="wrn-modal__panel"
role="dialog"
aria-modal="true"
aria-label='{label || title}'
tabindex="-1"
>
<header
class="wire-modal__header"
class="wrn-modal__header"
>
<div
class="wire-modal__heading"
class="wrn-modal__heading"
>
{#if icon}
<span
class='wire-modal__icon {icon}'
class='wrn-modal__icon {icon}'
aria-hidden="true"
>
</span>
{/if}
<div
class="wire-modal__heading-copy"
class="wrn-modal__heading-copy"
>
<slot
name="header"
@@ -192,7 +192,7 @@ component Modal {
{#if showClose}
<button
type="button"
class="wire-modal__close"
class="wrn-modal__close"
aria-label='{closeLabel}'
@click='hideModal("close-button", event)'
>
@@ -215,17 +215,17 @@ component Modal {
</header>
<div
class="wire-modal__body"
class="wrn-modal__body"
>
<slot></slot>
</div>
{#if showFooter}
<footer
class="wire-modal__footer"
class="wrn-modal__footer"
>
<div
class="wire-modal__custom-footer"
class="wrn-modal__custom-footer"
>
<slot
name="footer"
@@ -234,12 +234,12 @@ component Modal {
</div>
<div
class="wire-modal__actions"
class="wrn-modal__actions"
>
{#if cancelLabel}
<button
type="button"
class="wire-modal__button wire-modal__button--secondary"
class="wrn-modal__button wrn-modal__button--secondary"
@click='cancelModal(event)'
>
{#if cancelIcon}
@@ -256,13 +256,13 @@ component Modal {
{#if confirmLabel}
<button
type="button"
class="wire-modal__button wire-modal__button--primary"
class="wrn-modal__button wrn-modal__button--primary"
disabled='{confirmDisabled || confirmLoading}'
@click='confirmModal(event)'
>
{#if confirmLoading}
<span
class="wire-modal__spinner"
class="wrn-modal__spinner"
aria-hidden="true"
>
</span>
@@ -285,63 +285,63 @@ component Modal {
}
style {
.wire-modal {
--modal-accent: var(--wire-color-primary);
--modal-soft: var(--wire-color-primary-soft);
--modal-contrast: var(--wire-color-primary-contrast);
.wrn-modal {
--modal-accent: var(--wrn-color-primary);
--modal-soft: var(--wrn-color-primary-soft);
--modal-contrast: var(--wrn-color-primary-contrast);
position: relative;
display: inline-flex;
}
.wire-modal[data-color="secondary"] {
--modal-accent: var(--wire-color-secondary);
--modal-soft: var(--wire-color-secondary-soft);
--modal-contrast: var(--wire-color-secondary-contrast);
.wrn-modal[data-color="secondary"] {
--modal-accent: var(--wrn-color-secondary);
--modal-soft: var(--wrn-color-secondary-soft);
--modal-contrast: var(--wrn-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
* token defined at all, so var(--wrn-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, white);
.wrn-modal[data-color="info"] {
--modal-accent: var(--wrn-color-info);
--modal-soft: var(--wrn-color-info-soft);
--modal-contrast: var(--wrn-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, white);
.wrn-modal[data-color="success"] {
--modal-accent: var(--wrn-color-success);
--modal-soft: var(--wrn-color-success-soft);
--modal-contrast: var(--wrn-color-success-contrast, white);
}
.wire-modal[data-color="warning"] {
--modal-accent: var(--wire-color-warning);
--modal-soft: var(--wire-color-warning-soft);
--modal-contrast: var(--wire-color-warning-text);
.wrn-modal[data-color="warning"] {
--modal-accent: var(--wrn-color-warning);
--modal-soft: var(--wrn-color-warning-soft);
--modal-contrast: var(--wrn-color-warning-text);
}
.wire-modal[data-color="danger"],
.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, white);
.wrn-modal[data-color="danger"],
.wrn-modal[data-destructive="true"] {
--modal-accent: var(--wrn-color-danger);
--modal-soft: var(--wrn-color-danger-soft);
--modal-contrast: var(--wrn-color-on-danger, white);
}
.wire-modal__trigger,
.wire-modal__trigger-slot {
.wrn-modal__trigger,
.wrn-modal__trigger-slot {
display: inline-flex;
align-items: center;
gap: 0.55rem;
}
.wire-modal__trigger {
.wrn-modal__trigger {
appearance: none;
min-height: 2.55rem;
padding: 0.65rem 1rem;
@@ -356,22 +356,22 @@ component Modal {
transition: opacity 150ms ease, transform 150ms ease, box-shadow 150ms ease;
}
.wire-modal__trigger:hover {
.wrn-modal__trigger:hover {
opacity: 0.92;
}
.wire-modal__trigger:active {
.wrn-modal__trigger:active {
transform: scale(0.97);
}
.wire-modal__trigger:focus-visible {
.wrn-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
* hydration runs. data-open on the wrn-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
@@ -382,7 +382,7 @@ component Modal {
* animate -- a box that starts at display: none has no prior frame to
* transition from.
*/
.wire-modal__layer {
.wrn-modal__layer {
position: fixed;
inset: 0;
z-index: 1250;
@@ -395,24 +395,24 @@ component Modal {
transition: opacity 180ms ease, visibility 0s linear 180ms;
}
.wire-modal[data-open="true"] .wire-modal__layer {
.wrn-modal[data-open="true"] .wrn-modal__layer {
visibility: visible;
opacity: 1;
transition: opacity 180ms ease, visibility 0s linear 0s;
}
.wire-modal[data-placement="top"] .wire-modal__layer {
.wrn-modal[data-placement="top"] .wrn-modal__layer {
align-items: flex-start;
padding-top: clamp(1rem, 8vh, 5rem);
}
.wire-modal[data-scroll="page"] .wire-modal__layer {
.wrn-modal[data-scroll="page"] .wrn-modal__layer {
align-items: flex-start;
overflow-y: auto;
padding: 2.5rem 1rem;
}
.wire-modal__backdrop {
.wrn-modal__backdrop {
position: absolute;
inset: 0;
z-index: 0;
@@ -423,22 +423,22 @@ component Modal {
backdrop-filter: blur(9px);
}
.wire-modal__panel {
.wrn-modal__panel {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
width: min(32rem, calc(100vw - 2rem));
max-height: min(88vh, 52rem);
color: var(--wire-color-text);
color: var(--wrn-color-text);
background:
radial-gradient(
circle at 100% 0%,
color-mix(in srgb, var(--modal-accent) 9%, transparent),
transparent 34%
),
var(--wire-color-surface-raised);
border: 1px solid color-mix(in srgb, var(--modal-accent) 18%, var(--wire-color-border));
var(--wrn-color-surface-raised);
border: 1px solid color-mix(in srgb, var(--modal-accent) 18%, var(--wrn-color-border));
border-radius: 1.35rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
@@ -449,71 +449,71 @@ component Modal {
transition: opacity 180ms ease, transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
.wire-modal[data-open="true"] .wire-modal__panel {
.wrn-modal[data-open="true"] .wrn-modal__panel {
opacity: 1;
transform: none;
}
.wire-modal[data-size="xs"] .wire-modal__panel {
.wrn-modal[data-size="xs"] .wrn-modal__panel {
width: min(19rem, calc(100vw - 2rem));
}
.wire-modal[data-size="sm"] .wire-modal__panel {
.wrn-modal[data-size="sm"] .wrn-modal__panel {
width: min(25rem, calc(100vw - 2rem));
}
.wire-modal[data-size="lg"] .wire-modal__panel {
.wrn-modal[data-size="lg"] .wrn-modal__panel {
width: min(44rem, calc(100vw - 2rem));
}
.wire-modal[data-size="xl"] .wire-modal__panel {
.wrn-modal[data-size="xl"] .wrn-modal__panel {
width: min(64rem, calc(100vw - 2rem));
}
.wire-modal[data-size="full"] .wire-modal__panel {
.wrn-modal[data-size="full"] .wrn-modal__panel {
width: calc(100vw - 2rem);
height: calc(100vh - 2rem);
max-height: none;
}
.wire-modal[data-scroll="page"] .wire-modal__panel {
.wrn-modal[data-scroll="page"] .wrn-modal__panel {
max-height: none;
}
.wire-modal[data-variant="soft"] .wire-modal__panel {
.wrn-modal[data-variant="soft"] .wrn-modal__panel {
background:
linear-gradient(145deg, var(--modal-soft), transparent 68%),
var(--wire-color-surface-raised);
var(--wrn-color-surface-raised);
}
.wire-modal[data-variant="outline"] .wire-modal__panel {
background: var(--wire-color-surface-raised);
.wrn-modal[data-variant="outline"] .wrn-modal__panel {
background: var(--wrn-color-surface-raised);
box-shadow: 0 24px 72px color-mix(in srgb, black 24%, transparent);
}
.wire-modal[data-variant="solid"] .wire-modal__panel {
.wrn-modal[data-variant="solid"] .wrn-modal__panel {
color: var(--modal-contrast);
background: var(--modal-accent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-modal__header {
.wrn-modal__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
padding: 1.4rem 1.4rem 1.15rem;
border-bottom: 1px solid var(--wire-color-border);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-modal__heading {
.wrn-modal__heading {
display: flex;
align-items: flex-start;
gap: 0.9rem;
min-width: 0;
}
.wire-modal__icon {
.wrn-modal__icon {
flex: 0 0 auto;
width: 1.3rem;
height: 1.3rem;
@@ -521,35 +521,35 @@ component Modal {
color: var(--modal-accent);
}
.wire-modal[data-variant="solid"] .wire-modal__icon {
.wrn-modal[data-variant="solid"] .wrn-modal__icon {
color: currentColor;
}
.wire-modal__heading-copy {
.wrn-modal__heading-copy {
display: grid;
gap: 0.3rem;
min-width: 0;
}
.wire-modal__heading-copy h2,
.wire-modal__heading-copy p {
.wrn-modal__heading-copy h2,
.wrn-modal__heading-copy p {
margin: 0;
}
.wire-modal__heading-copy h2 {
.wrn-modal__heading-copy h2 {
color: inherit;
font-size: 1.08rem;
font-weight: 650;
line-height: 1.3;
}
.wire-modal__heading-copy p {
color: var(--wire-color-text-muted);
.wrn-modal__heading-copy p {
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
line-height: 1.55;
}
.wire-modal[data-variant="solid"] .wire-modal__heading-copy p {
.wrn-modal[data-variant="solid"] .wrn-modal__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
@@ -559,7 +559,7 @@ component Modal {
* 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 {
.wrn-modal__close {
appearance: none;
display: inline-flex;
align-items: center;
@@ -568,7 +568,7 @@ component Modal {
padding: 0;
width: 2.15rem;
height: 2.15rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
background: transparent;
border: 1px solid transparent;
border-radius: 9999px;
@@ -582,34 +582,34 @@ component Modal {
* 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 {
.wrn-modal[data-variant="solid"] .wrn-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 {
.wrn-modal[data-variant="solid"] .wrn-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);
.wrn-modal__close:hover {
color: var(--wrn-color-text);
background: var(--wrn-color-surface-soft);
}
.wire-modal__close:focus-visible {
.wrn-modal__close:focus-visible {
color: var(--modal-accent);
border-color: color-mix(in srgb, var(--modal-accent) 45%, transparent);
outline: none;
}
.wire-modal__close:active {
.wrn-modal__close:active {
transform: scale(0.92);
}
/* Never let the glyph be shrunk by the flex container. */
.wire-modal__close svg {
.wrn-modal__close svg {
flex: 0 0 auto;
width: 1rem;
height: 1rem;
@@ -628,46 +628,46 @@ component Modal {
* 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 {
.wrn-modal__body {
flex: 1 1 auto;
min-height: 0;
padding: 1.4rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
/*
* :where() keeps this at the specificity of .wire-modal__body alone, so it
* :where() keeps this at the specificity of .wrn-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.
* plain .wrn-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) {
.wrn-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 {
.wrn-modal[data-variant="solid"] .wrn-modal__body {
color: var(--modal-contrast);
}
.wire-modal[data-scrollable="true"] .wire-modal__body {
.wrn-modal[data-scrollable="true"] .wrn-modal__body {
overflow: auto;
overscroll-behavior: contain;
}
.wire-modal__footer {
.wrn-modal__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1rem 1.4rem 1.4rem;
border-top: 1px solid var(--wire-color-border);
border-top: 1px solid var(--wrn-color-border);
}
.wire-modal__custom-footer:empty {
.wrn-modal__custom-footer:empty {
display: none;
}
.wire-modal__actions {
.wrn-modal__actions {
display: flex;
align-items: center;
justify-content: flex-end;
@@ -675,7 +675,7 @@ component Modal {
margin-left: auto;
}
.wire-modal__button {
.wrn-modal__button {
appearance: none;
display: inline-flex;
align-items: center;
@@ -691,17 +691,17 @@ component Modal {
transition: transform 150ms ease, opacity 150ms ease, border-color 150ms ease;
}
.wire-modal__button:active {
.wrn-modal__button:active {
transform: scale(0.985);
}
.wire-modal__button--secondary {
color: var(--wire-color-text);
.wrn-modal__button--secondary {
color: var(--wrn-color-text);
background: transparent;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
}
.wire-modal__button--primary {
.wrn-modal__button--primary {
color: var(--modal-contrast);
background: var(--modal-accent);
border: 1px solid transparent;
@@ -713,73 +713,73 @@ component Modal {
* 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 {
.wrn-modal[data-variant="solid"] .wrn-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 {
.wrn-modal__button:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.wire-modal__spinner {
.wrn-modal__spinner {
width: 0.95rem;
height: 0.95rem;
border: 2px solid color-mix(in srgb, currentColor 35%, transparent);
border-top-color: currentColor;
border-radius: 50%;
animation: wire-modal-spin 750ms linear infinite;
animation: wrn-modal-spin 750ms linear infinite;
}
@keyframes wire-modal-spin {
@keyframes wrn-modal-spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 639px) {
.wire-modal__layer {
.wrn-modal__layer {
align-items: flex-end;
padding: 0;
}
.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 {
.wrn-modal__panel,
.wrn-modal[data-size="xs"] .wrn-modal__panel,
.wrn-modal[data-size="sm"] .wrn-modal__panel,
.wrn-modal[data-size="lg"] .wrn-modal__panel,
.wrn-modal[data-size="xl"] .wrn-modal__panel {
width: 100%;
max-height: 92vh;
border-radius: 1.25rem 1.25rem 0 0;
}
.wire-modal__footer {
.wrn-modal__footer {
align-items: stretch;
flex-direction: column;
}
.wire-modal__actions {
.wrn-modal__actions {
width: 100%;
}
.wire-modal__button {
.wrn-modal__button {
flex: 1 1 0;
}
}
@media (prefers-reduced-motion: reduce) {
.wire-modal__button,
.wire-modal__spinner,
.wire-modal__layer,
.wire-modal__panel,
.wire-modal__close {
.wrn-modal__button,
.wrn-modal__spinner,
.wrn-modal__layer,
.wrn-modal__panel,
.wrn-modal__close {
animation: none;
transition: none;
}
.wire-modal__panel {
.wrn-modal__panel {
transform: none;
}
}
+70 -70
View File
@@ -60,7 +60,7 @@ component Nav {
<nav
{...attrs}
data-ui-component="Nav"
class='wire-nav {class}'
class='wrn-nav {class}'
data-orientation='{orientation}'
data-color='{color}'
data-size='{size}'
@@ -70,21 +70,21 @@ component Nav {
>
<button
type="button"
class="wire-nav__toggle"
class="wrn-nav__toggle"
data-show="collapsible"
aria-expanded='{expanded}'
aria-label='{toggleLabel}'
@click='toggleMenu()'
>
<span class="wire-nav__toggle-bar" aria-hidden="true"></span>
<span class="wire-nav__toggle-text">{toggleLabel}</span>
<span class="wrn-nav__toggle-bar" aria-hidden="true"></span>
<span class="wrn-nav__toggle-text">{toggleLabel}</span>
</button>
<ul class="wire-nav__list" data-wrn-roving='{rovingAxis()}'>
<ul class="wrn-nav__list" data-wrn-roving='{rovingAxis()}'>
{#each itemList() as item}
<li class="wire-nav__item" data-has-children='{childrenOf(item).length > 0}'>
<li class="wrn-nav__item" data-has-children='{childrenOf(item).length > 0}'>
<a
class="wire-nav__link"
class="wrn-nav__link"
href='{item.href || "#"}'
data-wrn-roving-item="true"
data-active='{isActive(item)}'
@@ -93,28 +93,28 @@ component Nav {
@click='choose(item)'
>
<span
class='wire-nav__icon {item.icon}'
class='wrn-nav__icon {item.icon}'
data-show="item.icon"
aria-hidden="true"
></span>
<span class="wire-nav__label">{item.label}</span>
<span class="wire-nav__badge" data-show="item.badge">{item.badge}</span>
<span class="wrn-nav__label">{item.label}</span>
<span class="wrn-nav__badge" data-show="item.badge">{item.badge}</span>
<span
class="wire-nav__arrow"
class="wrn-nav__arrow"
data-show="childrenOf(item).length > 0"
aria-hidden="true"
>&#8250;</span>
</a>
<ul
class="wire-nav__submenu"
class="wrn-nav__submenu"
data-wrn-anchored="true"
data-show="childrenOf(item).length > 0"
>
{#each childrenOf(item) as child}
<li class="wire-nav__item" data-has-children='{childrenOf(child).length > 0}'>
<li class="wrn-nav__item" data-has-children='{childrenOf(child).length > 0}'>
<a
class="wire-nav__link"
class="wrn-nav__link"
href='{child.href || "#"}'
data-active='{isActive(child)}'
aria-current='{isActive(child) ? "page" : "false"}'
@@ -122,28 +122,28 @@ component Nav {
@click='choose(child)'
>
<span
class='wire-nav__icon {child.icon}'
class='wrn-nav__icon {child.icon}'
data-show="child.icon"
aria-hidden="true"
></span>
<span class="wire-nav__label">{child.label}</span>
<span class="wire-nav__badge" data-show="child.badge">{child.badge}</span>
<span class="wrn-nav__label">{child.label}</span>
<span class="wrn-nav__badge" data-show="child.badge">{child.badge}</span>
<span
class="wire-nav__arrow"
class="wrn-nav__arrow"
data-show="childrenOf(child).length > 0"
aria-hidden="true"
>&#8250;</span>
</a>
<ul
class="wire-nav__submenu wire-nav__submenu--level3"
class="wrn-nav__submenu wrn-nav__submenu--level3"
data-wrn-anchored="true"
data-show="childrenOf(child).length > 0"
>
{#each childrenOf(child) as leaf}
<li class="wire-nav__item">
<li class="wrn-nav__item">
<a
class="wire-nav__link"
class="wrn-nav__link"
href='{leaf.href || "#"}'
data-active='{isActive(leaf)}'
aria-current='{isActive(leaf) ? "page" : "false"}'
@@ -151,11 +151,11 @@ component Nav {
@click='choose(leaf)'
>
<span
class='wire-nav__icon {leaf.icon}'
class='wrn-nav__icon {leaf.icon}'
data-show="leaf.icon"
aria-hidden="true"
></span>
<span class="wire-nav__label">{leaf.label}</span>
<span class="wrn-nav__label">{leaf.label}</span>
</a>
</li>
{/each}
@@ -172,36 +172,36 @@ component Nav {
}
style {
.wire-nav {
--nav-accent: var(--wire-color-primary);
.wrn-nav {
--nav-accent: var(--wrn-color-primary);
max-width: 100%;
}
.wire-nav[data-color="secondary"] {
--nav-accent: var(--wire-color-secondary);
.wrn-nav[data-color="secondary"] {
--nav-accent: var(--wrn-color-secondary);
}
.wire-nav[data-color="success"] {
--nav-accent: var(--wire-color-success);
.wrn-nav[data-color="success"] {
--nav-accent: var(--wrn-color-success);
}
.wire-nav[data-color="danger"] {
--nav-accent: var(--wire-color-danger);
.wrn-nav[data-color="danger"] {
--nav-accent: var(--wrn-color-danger);
}
.wire-nav[data-color="info"] {
--nav-accent: var(--wire-color-info);
.wrn-nav[data-color="info"] {
--nav-accent: var(--wrn-color-info);
}
.wire-nav[data-size="sm"] {
.wrn-nav[data-size="sm"] {
font-size: 0.82rem;
}
.wire-nav[data-size="lg"] {
.wrn-nav[data-size="lg"] {
font-size: 1rem;
}
.wire-nav__list {
.wrn-nav__list {
display: flex;
align-items: center;
gap: 0.25rem;
@@ -210,48 +210,48 @@ component Nav {
list-style: none;
}
.wire-nav[data-orientation="vertical"] .wire-nav__list {
.wrn-nav[data-orientation="vertical"] .wrn-nav__list {
flex-direction: column;
align-items: stretch;
}
.wire-nav__item {
.wrn-nav__item {
position: relative;
}
.wire-nav__link {
.wrn-nav__link {
display: flex;
align-items: center;
gap: 0.45rem;
padding: 0.45rem 0.7rem;
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text-muted);
border-radius: var(--wrn-radius-sm);
color: var(--wrn-color-text-muted);
font-size: 0.9rem;
font-weight: 600;
text-decoration: none;
}
.wire-nav__link:hover {
background: var(--wire-color-surface-soft);
color: var(--wire-color-text);
.wrn-nav__link:hover {
background: var(--wrn-color-surface-soft);
color: var(--wrn-color-text);
}
.wire-nav__link:focus-visible {
.wrn-nav__link:focus-visible {
outline: 2px solid var(--nav-accent);
outline-offset: 2px;
}
.wire-nav__link[data-active="true"] {
.wrn-nav__link[data-active="true"] {
background: color-mix(in srgb, var(--nav-accent) 16%, transparent);
color: var(--nav-accent);
}
.wire-nav__link[aria-disabled="true"] {
.wrn-nav__link[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
}
.wire-nav__badge {
.wrn-nav__badge {
padding: 0.05rem 0.4rem;
border-radius: 999px;
background: color-mix(in srgb, var(--nav-accent) 16%, transparent);
@@ -259,17 +259,17 @@ component Nav {
font-size: 0.72rem;
}
.wire-nav__arrow {
.wrn-nav__arrow {
display: inline-block;
transition: transform 160ms ease;
}
.wire-nav__item:hover > .wire-nav__link > .wire-nav__arrow,
.wire-nav__item:focus-within > .wire-nav__link > .wire-nav__arrow {
.wrn-nav__item:hover > .wrn-nav__link > .wrn-nav__arrow,
.wrn-nav__item:focus-within > .wrn-nav__link > .wrn-nav__arrow {
transform: rotate(90deg);
}
.wire-nav__submenu {
.wrn-nav__submenu {
position: absolute;
z-index: 30;
top: 100%;
@@ -277,42 +277,42 @@ component Nav {
min-width: 12rem;
margin: 0;
padding: 0.35rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
list-style: none;
opacity: 0;
visibility: hidden;
transition: opacity 160ms ease;
}
.wire-nav__item:hover > .wire-nav__submenu,
.wire-nav__item:focus-within > .wire-nav__submenu {
.wrn-nav__item:hover > .wrn-nav__submenu,
.wrn-nav__item:focus-within > .wrn-nav__submenu {
opacity: 1;
visibility: visible;
}
.wire-nav__submenu--level3 {
.wrn-nav__submenu--level3 {
top: 0;
left: 100%;
}
.wire-nav__toggle {
.wrn-nav__toggle {
display: none;
align-items: center;
gap: 0.5rem;
padding: 0.45rem 0.7rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
font-size: 0.9rem;
cursor: pointer;
}
.wire-nav__toggle-bar {
.wrn-nav__toggle-bar {
width: 1rem;
height: 2px;
background: currentColor;
@@ -327,21 +327,21 @@ component Nav {
* unreachable on touch.
*/
@media (max-width: 767px) {
.wire-nav__toggle {
.wrn-nav__toggle {
display: inline-flex;
}
.wire-nav__list {
.wrn-nav__list {
flex-direction: column;
align-items: stretch;
}
.wire-nav[data-expanded="false"] .wire-nav__list {
.wrn-nav[data-expanded="false"] .wrn-nav__list {
display: none;
}
.wire-nav__submenu,
.wire-nav__submenu--level3 {
.wrn-nav__submenu,
.wrn-nav__submenu--level3 {
position: static;
opacity: 1;
visibility: visible;
+198 -132
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Navbar {
outputs {
toggle(payload: { open: boolean })
@@ -62,47 +60,47 @@ size: string = "default"
}
view {
<header class="wire-navbar wire-navbar--width-{maxWidth} wire-component--color-{color} wire-component--size-{size} {sticky ? 'wire-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}">
<div class="wire-navbar__topbar" aria-label="{topbarLabel}">
<header class="wrn-navbar wrn-navbar--width-{maxWidth} wrn-component--color-{color} wrn-component--size-{size} {sticky ? 'wrn-navbar--sticky' : ''} {class}" data-wrn-navbar data-open-on-hover="{openOnHover}">
<div class="wrn-navbar__topbar" aria-label="{topbarLabel}">
<slot name="topbar" />
</div>
<div class="wire-navbar__main">
<a class="wire-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}">
<div class="wrn-navbar__main">
<a class="wrn-navbar__brand" href="{brand.href || '/'}" aria-label="{brand.ariaLabel || brand.label || label}">
{#if brand.logo}
<img class="wire-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" />
<img class="wrn-navbar__brand-logo" src="{brand.logo}" alt="{brand.logoAlt || brand.label || ''}" width="{brand.logoWidth || ''}" height="{brand.logoHeight || ''}" />
{/if}
{#if brand.icon}
<span class="wire-navbar__brand-icon {brand.icon}" aria-hidden="true"></span>
<span class="wrn-navbar__brand-icon {brand.icon}" aria-hidden="true"></span>
{/if}
{#if brand.label || brand.description}
<span class="wire-navbar__brand-copy">
<span class="wrn-navbar__brand-copy">
{#if brand.label}<strong>{brand.label}</strong>{/if}
{#if brand.description}<small>{brand.description}</small>{/if}
</span>
{/if}
</a>
<button type="button" class="wire-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation()">
<button type="button" class="wrn-navbar__toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleNavigation()">
<span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span>
</button>
<div class="wire-navbar__collapse {mobileOpen ? 'is-open' : ''}">
<nav class="wire-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
<div class="wrn-navbar__collapse {mobileOpen ? 'is-open' : ''}">
<nav class="wrn-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
{#each items as item}
{#if item.children && item.children.length}
<details class="wire-navbar__dropdown wire-navbar__dropdown--{item.type || 'dropdown'}" name="wire-navbar-menu" @toggle="toggleDropdown(event, item)">
<details class="wrn-navbar__dropdown wrn-navbar__dropdown--{item.type || 'dropdown'}" name="wrn-navbar-menu" @toggle="toggleDropdown(event, item)">
<summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span>{item.label}</span>
<span class="wire-navbar__chevron" aria-hidden="true"></span>
<span class="wrn-navbar__chevron" aria-hidden="true"></span>
</summary>
<div class="wire-navbar__panel wire-navbar__panel--columns-{item.columns || 1}">
{#if item.description}<p class="wire-navbar__panel-intro">{item.description}</p>{/if}
<div class="wrn-navbar__panel wrn-navbar__panel--columns-{item.columns || 1}">
{#if item.description}<p class="wrn-navbar__panel-intro">{item.description}</p>{/if}
{#each item.children as child}
<div class="wire-navbar__group">
<div class="wrn-navbar__group">
{#if child.children && child.children.length}
{#if child.label}<strong class="wire-navbar__group-title">{child.label}</strong>{/if}
{#if child.label}<strong class="wrn-navbar__group-title">{child.label}</strong>{/if}
{#if child.description}<small>{child.description}</small>{/if}
{#each child.children as nested}
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3)">
@@ -121,7 +119,7 @@ size: string = "default"
</div>
</details>
{:else}
<a class="wire-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)">
<a class="wrn-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span>{item.label}</span>
</a>
@@ -129,9 +127,9 @@ size: string = "default"
{/each}
</nav>
<div class="wire-navbar__actions">
<div class="wrn-navbar__actions">
{#each actions as item}
<a class="wire-navbar__action wire-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item)">
<a class="wrn-navbar__action wrn-navbar__action--{item.variant || 'link'}" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" @click="selectAction(item)">
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
<span>{item.label}</span>
</a>
@@ -141,46 +139,45 @@ size: string = "default"
</div>
</div>
</header>
<ComponentBaseStyles hidden />
}
style {
/* Full application navigation */
.wire-navbar {
.wrn-navbar {
position: relative;
z-index: 40;
width: 100%;
color: var(--wire-color-text);
background: color-mix(in srgb, var(--wire-color-surface) 96%, transparent);
border-bottom: 1px solid var(--wire-color-border);
color: var(--wrn-color-text);
background: color-mix(in srgb, var(--wrn-color-surface) 96%, transparent);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-navbar--sticky {
.wrn-navbar--sticky {
position: sticky;
top: 0;
backdrop-filter: blur(16px);
}
.wire-navbar__topbar:empty,
.wire-navbar__topbar:not(:has(*)) {
.wrn-navbar__topbar:empty,
.wrn-navbar__topbar:not(:has(*)) {
display: none;
}
.wire-navbar__topbar:not(:empty) {
.wrn-navbar__topbar:not(:empty) {
display: flex;
min-height: 2.25rem;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.35rem clamp(1rem, 4vw, 3rem);
color: var(--wire-color-muted);
background: var(--wire-color-surface-2);
border-bottom: 1px solid var(--wire-color-border);
color: var(--wrn-color-muted);
background: var(--wrn-color-surface-2);
border-bottom: 1px solid var(--wrn-color-border);
font-size: 0.75rem;
font-weight: 400;
}
.wire-navbar__main {
.wrn-navbar__main {
display: flex;
min-height: 4.25rem;
width: min(100%, 90rem);
@@ -190,25 +187,25 @@ size: string = "default"
gap: 1.5rem;
}
.wire-navbar--width-full .wire-navbar__main {
.wrn-navbar--width-full .wrn-navbar__main {
width: 100%;
max-width: none;
}
.wire-navbar--width-compact .wire-navbar__main {
.wrn-navbar--width-compact .wrn-navbar__main {
width: min(86%, 80rem);
}
.wire-navbar__brand,
.wire-navbar__menu-link,
.wire-navbar__dropdown summary,
.wire-navbar__action,
.wire-navbar__panel a {
.wrn-navbar__brand,
.wrn-navbar__menu-link,
.wrn-navbar__dropdown summary,
.wrn-navbar__action,
.wrn-navbar__panel a {
color: inherit;
text-decoration: none;
}
.wire-navbar__brand {
.wrn-navbar__brand {
display: inline-flex;
min-width: 0;
align-items: center;
@@ -216,7 +213,7 @@ size: string = "default"
flex: 0 0 auto;
}
.wire-navbar__brand-logo {
.wrn-navbar__brand-logo {
display: block;
width: auto;
max-width: 11rem;
@@ -224,57 +221,57 @@ size: string = "default"
object-fit: contain;
}
.wire-navbar__brand-icon {
.wrn-navbar__brand-icon {
width: 2.5rem;
height: 2.5rem;
color: var(--wire-component-color, var(--wire-color-primary));
color: var(--wrn-component-color, var(--wrn-color-primary));
}
.wire-navbar__brand-copy {
.wrn-navbar__brand-copy {
display: grid;
min-width: 0;
line-height: 1.2;
}
.wire-navbar__brand-copy strong {
.wrn-navbar__brand-copy strong {
font-size: 0.9rem;
font-weight: 600;
}
.wire-navbar__brand-copy small {
.wrn-navbar__brand-copy small {
margin-top: 0.2rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.6875rem;
font-weight: 400;
}
.wire-navbar__collapse,
.wire-navbar__menus,
.wire-navbar__actions {
.wrn-navbar__collapse,
.wrn-navbar__menus,
.wrn-navbar__actions {
display: flex;
align-items: center;
}
.wire-navbar__collapse {
.wrn-navbar__collapse {
min-width: 0;
flex: 1;
gap: 1rem;
}
.wire-navbar__menus {
.wrn-navbar__menus {
justify-content: center;
flex: 1;
gap: 0.25rem;
}
.wire-navbar__actions {
.wrn-navbar__actions {
justify-content: flex-end;
gap: 0.5rem;
}
.wire-navbar__menu-link,
.wire-navbar__dropdown > summary,
.wire-navbar__action {
.wrn-navbar__menu-link,
.wrn-navbar__dropdown > summary,
.wrn-navbar__action {
display: inline-flex;
min-height: 2.75rem;
padding: 0.65rem 0.8rem;
@@ -287,60 +284,60 @@ size: string = "default"
white-space: nowrap;
}
.wire-navbar__menu-link:hover,
.wire-navbar__dropdown > summary:hover {
color: var(--wire-color-text);
background: var(--wire-color-surface-2);
.wrn-navbar__menu-link:hover,
.wrn-navbar__dropdown > summary:hover {
color: var(--wrn-color-text);
background: var(--wrn-color-surface-2);
}
.wire-navbar__menu-link[aria-current="page"],
.wire-navbar__dropdown > summary[aria-current="page"] {
color: var(--wire-component-color, var(--wire-color-primary));
.wrn-navbar__menu-link[aria-current="page"],
.wrn-navbar__dropdown > summary[aria-current="page"] {
color: var(--wrn-component-color, var(--wrn-color-primary));
font-weight: 600;
background: color-mix(
in srgb,
var(--wire-component-color, var(--wire-color-primary)) 12%,
var(--wrn-component-color, var(--wrn-color-primary)) 12%,
transparent
);
box-shadow: inset 0 -2px 0 var(--wire-component-color, var(--wire-color-primary));
box-shadow: inset 0 -2px 0 var(--wrn-component-color, var(--wrn-color-primary));
}
.wire-navbar__menu-link[aria-current="page"]:hover,
.wire-navbar__dropdown > summary[aria-current="page"]:hover {
color: var(--wire-component-color, var(--wire-color-primary));
.wrn-navbar__menu-link[aria-current="page"]:hover,
.wrn-navbar__dropdown > summary[aria-current="page"]:hover {
color: var(--wrn-component-color, var(--wrn-color-primary));
background: color-mix(
in srgb,
var(--wire-component-color, var(--wire-color-primary)) 16%,
var(--wrn-component-color, var(--wrn-color-primary)) 16%,
transparent
);
}
.wire-navbar__dropdown {
.wrn-navbar__dropdown {
position: relative;
}
.wire-navbar__dropdown > summary {
.wrn-navbar__dropdown > summary {
list-style: none;
}
.wire-navbar__dropdown > summary::-webkit-details-marker {
.wrn-navbar__dropdown > summary::-webkit-details-marker {
display: none;
}
.wire-navbar__chevron {
.wrn-navbar__chevron {
width: 0.45rem;
height: 0.45rem;
border-right: 1.5px solid currentColor;
border-bottom: 1.5px solid currentColor;
transform: rotate(45deg) translateY(-0.15rem);
transition: transform var(--wire-motion-fast, 150ms) ease;
transition: transform var(--wrn-motion-fast, 150ms) ease;
}
.wire-navbar__dropdown[open] .wire-navbar__chevron {
.wrn-navbar__dropdown[open] .wrn-navbar__chevron {
transform: rotate(225deg) translate(-0.1rem, -0.1rem);
}
.wire-navbar__panel {
.wrn-navbar__panel {
position: absolute;
top: calc(100% + 0.55rem);
left: 50%;
@@ -350,10 +347,10 @@ size: string = "default"
max-width: min(90vw, 64rem);
padding: 0.65rem;
gap: 0.45rem;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 0.9rem;
color: var(--wire-color-text);
background: var(--wire-color-surface);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
box-shadow: 0 18px 48px rgb(15 23 42 / 0.16);
opacity: 1;
transform: translateX(-50%) translateY(0) scale(1);
@@ -364,7 +361,7 @@ size: string = "default"
display 180ms allow-discrete;
}
.wire-navbar__panel::before {
.wrn-navbar__panel::before {
position: absolute;
right: 0;
bottom: 100%;
@@ -373,57 +370,57 @@ size: string = "default"
content: "";
}
.wire-navbar__dropdown:not([open]) > .wire-navbar__panel {
.wrn-navbar__dropdown:not([open]) > .wrn-navbar__panel {
display: none;
opacity: 0;
transform: translateX(-50%) translateY(-0.5rem) scale(0.98);
}
@starting-style {
.wire-navbar__dropdown[open] > .wire-navbar__panel {
.wrn-navbar__dropdown[open] > .wrn-navbar__panel {
opacity: 0;
transform: translateX(-50%) translateY(-0.5rem) scale(0.98);
}
}
.wire-navbar__dropdown--mega {
.wrn-navbar__dropdown--mega {
position: static;
}
.wire-navbar__dropdown--mega .wire-navbar__panel {
.wrn-navbar__dropdown--mega .wrn-navbar__panel {
right: auto;
left: 50%;
width: min(calc(100vw - 2rem), 64rem);
}
.wire-navbar__panel--columns-2 {
.wrn-navbar__panel--columns-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.wire-navbar__panel--columns-3 {
.wrn-navbar__panel--columns-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.wire-navbar__panel--columns-4 {
.wrn-navbar__panel--columns-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.wire-navbar__panel-intro {
.wrn-navbar__panel-intro {
grid-column: 1 / -1;
margin: 0;
padding: 0.5rem 0.65rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.8125rem;
font-weight: 400;
}
.wire-navbar__group {
.wrn-navbar__group {
display: grid;
align-content: start;
gap: 0.25rem;
}
.wire-navbar__group-title {
.wrn-navbar__group-title {
padding: 0.55rem 0.65rem 0.25rem;
font-size: 0.7rem;
font-weight: 600;
@@ -431,14 +428,14 @@ size: string = "default"
text-transform: uppercase;
}
.wire-navbar__group > small {
.wrn-navbar__group > small {
padding: 0 0.65rem 0.4rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.7rem;
font-weight: 400;
}
.wire-navbar__panel a {
.wrn-navbar__panel a {
display: flex;
min-height: 2.75rem;
padding: 0.65rem;
@@ -449,52 +446,52 @@ size: string = "default"
font-weight: 450;
}
.wire-navbar__panel a:hover {
background: var(--wire-color-surface-2);
.wrn-navbar__panel a:hover {
background: var(--wrn-color-surface-2);
}
.wire-navbar__panel a span:last-child {
.wrn-navbar__panel a span:last-child {
display: grid;
gap: 0.15rem;
}
.wire-navbar__panel a strong {
.wrn-navbar__panel a strong {
font-weight: 500;
}
.wire-navbar__panel a small {
color: var(--wire-color-muted);
.wrn-navbar__panel a small {
color: var(--wrn-color-muted);
font-size: 0.7rem;
font-weight: 400;
}
.wire-navbar__action {
.wrn-navbar__action {
border: 1px solid transparent;
}
.wire-navbar__action--primary {
color: var(--wire-color-on-primary, #fff);
background: var(--wire-component-color, var(--wire-color-primary));
.wrn-navbar__action--primary {
color: var(--wrn-color-on-primary, #fff);
background: var(--wrn-component-color, var(--wrn-color-primary));
font-weight: 600;
}
.wire-navbar__action--outline {
border-color: var(--wire-color-border);
.wrn-navbar__action--outline {
border-color: var(--wrn-color-border);
}
.wire-navbar__toggle {
.wrn-navbar__toggle {
display: none;
width: 2.75rem;
height: 2.75rem;
margin-left: auto;
padding: 0.65rem;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 0.65rem;
color: inherit;
background: var(--wire-color-surface);
background: var(--wrn-color-surface);
}
.wire-navbar__toggle span {
.wrn-navbar__toggle span {
display: block;
height: 2px;
margin: 0.25rem 0;
@@ -502,13 +499,13 @@ size: string = "default"
}
@media (max-width: 900px) {
.wire-navbar--width-compact .wire-navbar__main {
.wrn-navbar--width-compact .wrn-navbar__main {
width: 100%;
}
.wire-navbar__toggle {
.wrn-navbar__toggle {
display: block;
}
.wire-navbar__collapse {
.wrn-navbar__collapse {
position: absolute;
top: 100%;
right: 0;
@@ -517,30 +514,30 @@ size: string = "default"
padding: 0.75rem 1rem 1rem;
align-items: stretch;
flex-direction: column;
background: var(--wire-color-surface);
border-bottom: 1px solid var(--wire-color-border);
background: var(--wrn-color-surface);
border-bottom: 1px solid var(--wrn-color-border);
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
max-height: calc(100dvh - 4rem);
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
}
.wire-navbar__collapse.is-open {
.wrn-navbar__collapse.is-open {
display: flex;
}
.wire-navbar__menus,
.wire-navbar__actions {
.wrn-navbar__menus,
.wrn-navbar__actions {
width: 100%;
align-items: stretch;
flex-direction: column;
}
.wire-navbar__menu-link,
.wire-navbar__dropdown > summary,
.wire-navbar__action {
.wrn-navbar__menu-link,
.wrn-navbar__dropdown > summary,
.wrn-navbar__action {
width: 100%;
}
.wire-navbar__panel,
.wire-navbar__dropdown--mega .wire-navbar__panel {
.wrn-navbar__panel,
.wrn-navbar__dropdown--mega .wrn-navbar__panel {
position: static;
width: 100%;
max-width: none;
@@ -548,27 +545,27 @@ size: string = "default"
box-shadow: none;
transform: none;
overflow: hidden;
animation: wire-navbar-mobile-panel 180ms ease both;
animation: wrn-navbar-mobile-panel 180ms ease both;
}
.wire-navbar__dropdown:not([open]) > .wire-navbar__panel,
.wire-navbar__dropdown[open] > .wire-navbar__panel {
.wrn-navbar__dropdown:not([open]) > .wrn-navbar__panel,
.wrn-navbar__dropdown[open] > .wrn-navbar__panel {
transform: none;
}
.wire-navbar__panel[class*="wire-navbar__panel--columns-"] {
.wrn-navbar__panel[class*="wrn-navbar__panel--columns-"] {
grid-template-columns: 1fr;
}
.wire-navbar__panel::before {
.wrn-navbar__panel::before {
display: none;
}
}
@media (max-width: 600px) {
.wire-navbar__brand-copy small {
.wrn-navbar__brand-copy small {
display: none;
}
}
@keyframes wire-navbar-mobile-panel {
@keyframes wrn-navbar-mobile-panel {
from {
opacity: 0;
transform: translateY(-0.35rem);
@@ -578,5 +575,74 @@ size: string = "default"
transform: translateY(0);
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+51 -51
View File
@@ -104,35 +104,35 @@ component Pagination {
<nav
{...attrs}
data-ui-component="Pagination"
class='wire-pagination {class}'
class='wrn-pagination {class}'
data-variant='{variant}'
data-color='{color}'
data-size='{size}'
role="navigation"
aria-label='{label}'
>
<p class="wire-pagination__summary" data-show="showSummary">
<p class="wrn-pagination__summary" data-show="showSummary">
{firstShown()} to {lastShown()} of {total}
</p>
<div class="wire-pagination__controls">
<div class="wrn-pagination__controls">
<button
type="button"
class="wire-pagination__step"
class="wrn-pagination__step"
aria-label='{previousLabel}'
@click='goPrevious()'
>
<span class="wire-pagination__step-icon" aria-hidden="true">&#8249;</span>
<span class="wire-pagination__step-label">{previousLabel}</span>
<span class="wrn-pagination__step-icon" aria-hidden="true">&#8249;</span>
<span class="wrn-pagination__step-label">{previousLabel}</span>
</button>
<ol class="wire-pagination__pages" data-show="variant === 'numbered'">
<ol class="wrn-pagination__pages" data-show="variant === 'numbered'">
{#each pageNumbers() as entry}
<li class="wire-pagination__slot">
<span class="wire-pagination__gap" data-show="entry.gap">{entry.label}</span>
<li class="wrn-pagination__slot">
<span class="wrn-pagination__gap" data-show="entry.gap">{entry.label}</span>
<button
type="button"
class="wire-pagination__page"
class="wrn-pagination__page"
data-show="!entry.gap"
data-active='{entry.value === currentPage()}'
aria-current='{entry.value === currentPage() ? "page" : "false"}'
@@ -144,18 +144,18 @@ component Pagination {
{/each}
</ol>
<p class="wire-pagination__compact" data-show="variant !== 'numbered'">
<p class="wrn-pagination__compact" data-show="variant !== 'numbered'">
{currentPage()} / {lastPage()}
</p>
<button
type="button"
class="wire-pagination__step"
class="wrn-pagination__step"
aria-label='{nextLabel}'
@click='goNext()'
>
<span class="wire-pagination__step-label">{nextLabel}</span>
<span class="wire-pagination__step-icon" aria-hidden="true">&#8250;</span>
<span class="wrn-pagination__step-label">{nextLabel}</span>
<span class="wrn-pagination__step-icon" aria-hidden="true">&#8250;</span>
</button>
</div>
@@ -164,8 +164,8 @@ component Pagination {
}
style {
.wire-pagination {
--pagination-accent: var(--wire-color-primary);
.wrn-pagination {
--pagination-accent: var(--wrn-color-primary);
display: flex;
flex-wrap: wrap;
align-items: center;
@@ -174,44 +174,44 @@ component Pagination {
max-width: 100%;
}
.wire-pagination[data-color="secondary"] {
--pagination-accent: var(--wire-color-secondary);
.wrn-pagination[data-color="secondary"] {
--pagination-accent: var(--wrn-color-secondary);
}
.wire-pagination[data-color="success"] {
--pagination-accent: var(--wire-color-success);
.wrn-pagination[data-color="success"] {
--pagination-accent: var(--wrn-color-success);
}
.wire-pagination[data-color="danger"] {
--pagination-accent: var(--wire-color-danger);
.wrn-pagination[data-color="danger"] {
--pagination-accent: var(--wrn-color-danger);
}
.wire-pagination[data-color="info"] {
--pagination-accent: var(--wire-color-info);
.wrn-pagination[data-color="info"] {
--pagination-accent: var(--wrn-color-info);
}
.wire-pagination[data-size="sm"] {
.wrn-pagination[data-size="sm"] {
font-size: 0.82rem;
}
.wire-pagination[data-size="lg"] {
.wrn-pagination[data-size="lg"] {
font-size: 1rem;
}
.wire-pagination__summary {
.wrn-pagination__summary {
margin: 0;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.82rem;
}
.wire-pagination__controls {
.wrn-pagination__controls {
display: flex;
align-items: center;
gap: 0.35rem;
flex-wrap: wrap;
}
.wire-pagination__pages {
.wrn-pagination__pages {
display: flex;
align-items: center;
gap: 0.25rem;
@@ -220,12 +220,12 @@ component Pagination {
list-style: none;
}
.wire-pagination__slot {
.wrn-pagination__slot {
display: inline-flex;
}
.wire-pagination__page,
.wire-pagination__step {
.wrn-pagination__page,
.wrn-pagination__step {
appearance: none;
display: inline-flex;
align-items: center;
@@ -233,56 +233,56 @@ component Pagination {
min-width: 2.25rem;
justify-content: center;
padding: 0.4rem 0.6rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
font-size: 0.85rem;
cursor: pointer;
}
.wire-pagination__page:hover,
.wire-pagination__step:hover {
background: var(--wire-color-surface-soft);
.wrn-pagination__page:hover,
.wrn-pagination__step:hover {
background: var(--wrn-color-surface-soft);
}
.wire-pagination__page:focus-visible,
.wire-pagination__step:focus-visible {
.wrn-pagination__page:focus-visible,
.wrn-pagination__step:focus-visible {
outline: 2px solid var(--pagination-accent);
outline-offset: 2px;
}
.wire-pagination__page[data-active="true"] {
.wrn-pagination__page[data-active="true"] {
border-color: var(--pagination-accent);
background: var(--pagination-accent);
color: var(--wire-color-primary-contrast);
color: var(--wrn-color-primary-contrast);
font-weight: 700;
}
.wire-pagination__gap {
.wrn-pagination__gap {
padding: 0 0.35rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
}
.wire-pagination__compact {
.wrn-pagination__compact {
margin: 0;
padding: 0 0.5rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.85rem;
}
/* Below the small breakpoint the word labels crowd the arrows out. */
@media (max-width: 639px) {
.wire-pagination {
.wrn-pagination {
justify-content: center;
}
.wire-pagination__step-label {
.wrn-pagination__step-label {
display: none;
}
.wire-pagination__summary {
.wrn-pagination__summary {
width: 100%;
text-align: center;
}
+109 -43
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component PinInput {
outputs {
input(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)
@@ -51,7 +49,7 @@ component PinInput {
view {
<fieldset
{...attrs}
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--pin-input {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--pin-input {invalid ? 'wrn-next--invalid' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}"
data-wrn-pin-input
data-length="{length}"
data-pattern="{pattern}"
@@ -61,11 +59,11 @@ component PinInput {
disabled="{disabled}"
aria-describedby="{validationMessage ? `${name}-validation` : helpText ? `${name}-help` : ''}"
>
<div class="wire-next__pin-heading">
<div class="wrn-next__pin-heading">
<legend>{label}</legend>
<button
type="button"
class="wire-next__pin-clear"
class="wrn-next__pin-clear"
data-pin-clear
aria-label="{clearLabel}"
title="{clearLabel}"
@@ -76,7 +74,7 @@ component PinInput {
</button>
</div>
<div class="wire-next__pin-cells" role="group" aria-label="{label}">
<div class="wrn-next__pin-cells" role="group" aria-label="{label}">
{#each inputIndexes() as index}
<input
data-pin-cell
@@ -95,7 +93,7 @@ component PinInput {
autofocus="{autoFocus && index === 0}"
/>
{#if separator && groupSize !== 0 && (index + 1) % groupSize === 0 && index + 1 !== length}
<span class="wire-next__pin-separator" aria-hidden="true">{separator}</span>
<span class="wrn-next__pin-separator" aria-hidden="true">{separator}</span>
{/if}
{/each}
</div>
@@ -111,49 +109,48 @@ component PinInput {
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
class="wrn-next__validation"
data-error="{name}"
>{validationMessage}</small>
</fieldset>
<ComponentBaseStyles hidden />
}
style {
.wire-next--pin-input.wire-next--invalid .wire-next__pin-cells input,
.wire-next--pin-input:has(input.wire-invalid) .wire-next__pin-cells input {
border-color: var(--wire-color-danger);
.wrn-next--pin-input.wrn-next--invalid .wrn-next__pin-cells input,
.wrn-next--pin-input:has(input.wrn-invalid) .wrn-next__pin-cells input {
border-color: var(--wrn-color-danger);
}
.wire-next--pin-input {
.wrn-next--pin-input {
display: grid;
width: 100%;
min-width: 0;
gap: 0.65rem;
padding: 0;
border: 0;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-next__pin-heading {
.wrn-next__pin-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.wire-next__pin-heading legend {
.wrn-next__pin-heading legend {
padding: 0;
font-size: 0.82em;
font-weight: 700;
}
.wire-next__pin-clear {
.wrn-next__pin-clear {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0;
border: 0;
color: var(--wire-component-color);
color: var(--wrn-component-color);
background: transparent;
font: inherit;
font-size: 0.7em;
@@ -161,12 +158,12 @@ component PinInput {
cursor: pointer;
}
.wire-next__pin-clear > span:first-child {
.wrn-next__pin-clear > span:first-child {
width: 0.9rem;
height: 0.9rem;
}
.wire-next__pin-cells {
.wrn-next__pin-cells {
display: flex;
max-width: 100%;
align-items: center;
@@ -176,66 +173,135 @@ component PinInput {
scrollbar-width: thin;
}
.wire-next--pin-input .wire-next__pin-cells input {
.wrn-next--pin-input .wrn-next__pin-cells input {
flex: 0 0 clamp(2.4rem, 8vw, 3.25rem);
width: clamp(2.4rem, 8vw, 3.25rem);
height: clamp(2.75rem, 9vw, 3.5rem);
min-width: clamp(2.4rem, 8vw, 3.25rem);
padding: 0;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text);
background: var(--wire-color-bg);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-size: clamp(1rem, 3vw, 1.25rem);
font-weight: 750;
text-align: center;
caret-color: var(--wire-component-color);
caret-color: var(--wrn-component-color);
transition:
border-color var(--wire-motion-base) var(--wire-ease-standard),
box-shadow var(--wire-motion-base) var(--wire-ease-standard),
transform var(--wire-motion-fast) var(--wire-ease-standard);
border-color var(--wrn-motion-base) var(--wrn-ease-standard),
box-shadow var(--wrn-motion-base) var(--wrn-ease-standard),
transform var(--wrn-motion-fast) var(--wrn-ease-standard);
}
.wire-next--pin-input .wire-next__pin-cells input::placeholder {
color: var(--wire-color-muted);
.wrn-next--pin-input .wrn-next__pin-cells input::placeholder {
color: var(--wrn-color-muted);
opacity: 0.45;
}
.wire-next--pin-input .wire-next__pin-cells input:focus-visible {
border-color: var(--wire-component-color);
.wrn-next--pin-input .wrn-next__pin-cells input:focus-visible {
border-color: var(--wrn-component-color);
outline: 0;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-component-color) 18%, transparent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-component-color) 18%, transparent);
transform: translateY(-1px);
}
.wire-next--pin-input .wire-next__pin-cells input:disabled,
.wire-next--pin-input .wire-next__pin-cells input:read-only {
.wrn-next--pin-input .wrn-next__pin-cells input:disabled,
.wrn-next--pin-input .wrn-next__pin-cells input:read-only {
cursor: not-allowed;
opacity: 0.55;
}
.wire-next__pin-separator {
color: var(--wire-color-muted);
.wrn-next__pin-separator {
color: var(--wrn-color-muted);
font-size: 1.1rem;
font-weight: 800;
}
.wire-next--pin-input[data-complete="true"] .wire-next__pin-cells input {
border-color: color-mix(in srgb, var(--wire-component-color) 72%, var(--wire-color-border));
background: color-mix(in srgb, var(--wire-component-color) 7%, var(--wire-color-bg));
.wrn-next--pin-input[data-complete="true"] .wrn-next__pin-cells input {
border-color: color-mix(in srgb, var(--wrn-component-color) 72%, var(--wrn-color-border));
background: color-mix(in srgb, var(--wrn-component-color) 7%, var(--wrn-color-bg));
}
@media (max-width: 480px) {
.wire-next__pin-cells {
.wrn-next__pin-cells {
gap: 0.3rem;
}
.wire-next--pin-input .wire-next__pin-cells input {
.wrn-next--pin-input .wrn-next__pin-cells input {
flex-basis: min(2.7rem, 12vw);
width: min(2.7rem, 12vw);
min-width: min(2.7rem, 12vw);
height: min(3rem, 14vw);
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+98 -98
View File
@@ -91,12 +91,12 @@ component Popover {
data-size='{size}'
data-color='{color}'
data-variant='{variant}'
class='wire-popover {class}'
class='wrn-popover {class}'
@keydown='handleKeydown(event)'
>
<button
type="button"
class="wire-popover__trigger"
class="wrn-popover__trigger"
disabled='{disabled}'
aria-haspopup="dialog"
aria-expanded='{open || visible ? "true" : "false"}'
@@ -114,7 +114,7 @@ component Popover {
{#if closeOnOutside}
<button
type="button"
class="wire-popover__dismiss-layer"
class="wrn-popover__dismiss-layer"
data-show='{open || visible}'
aria-label='{closeLabel}'
@click='hidePopover("outside", event)'
@@ -122,23 +122,23 @@ component Popover {
{/if}
<section
class="wire-popover__panel"
class="wrn-popover__panel"
data-wrn-anchored="true"
data-show='{open || visible}'
role="dialog"
aria-label='{title || triggerLabel}'
>
{#if showArrow}
<span class="wire-popover__arrow" data-wrn-anchor-arrow="true" aria-hidden="true"></span>
<span class="wrn-popover__arrow" data-wrn-anchor-arrow="true" aria-hidden="true"></span>
{/if}
{#if title || description || icon || showClose}
<header class="wire-popover__header">
<div class="wire-popover__heading">
<header class="wrn-popover__header">
<div class="wrn-popover__heading">
{#if icon}
<span class='wire-popover__icon {icon}' aria-hidden="true"></span>
<span class='wrn-popover__icon {icon}' aria-hidden="true"></span>
{/if}
<div class="wire-popover__heading-copy">
<div class="wrn-popover__heading-copy">
<slot name="header"></slot>
{#if title}
<h3>{title}</h3>
@@ -152,7 +152,7 @@ component Popover {
{#if showClose}
<button
type="button"
class="wire-popover__close"
class="wrn-popover__close"
aria-label='{closeLabel}'
@click='hidePopover("close-button", event)'
>
@@ -162,16 +162,16 @@ component Popover {
</header>
{/if}
<div class="wire-popover__body">
<div class="wrn-popover__body">
<slot></slot>
</div>
{#if actionLabel}
<footer class="wire-popover__footer">
<footer class="wrn-popover__footer">
{#if actionHref}
<a
href='{actionHref}'
class="wire-popover__action"
class="wrn-popover__action"
@click='activateAction(event)'
>
<span>{actionLabel}</span>
@@ -184,7 +184,7 @@ component Popover {
{:else}
<button
type="button"
class="wire-popover__action"
class="wrn-popover__action"
@click='activateAction(event)'
>
<span>{actionLabel}</span>
@@ -204,45 +204,45 @@ component Popover {
}
style {
.wire-popover {
--popover-accent: var(--wire-color-primary);
--popover-soft: var(--wire-color-primary-soft);
--popover-contrast: var(--wire-color-primary-contrast);
.wrn-popover {
--popover-accent: var(--wrn-color-primary);
--popover-soft: var(--wrn-color-primary-soft);
--popover-contrast: var(--wrn-color-primary-contrast);
position: relative;
display: inline-flex;
}
.wire-popover[data-color="secondary"] {
--popover-accent: var(--wire-color-secondary);
--popover-soft: var(--wire-color-secondary-soft);
--popover-contrast: var(--wire-color-secondary-contrast);
.wrn-popover[data-color="secondary"] {
--popover-accent: var(--wrn-color-secondary);
--popover-soft: var(--wrn-color-secondary-soft);
--popover-contrast: var(--wrn-color-secondary-contrast);
}
.wire-popover[data-color="info"] {
--popover-accent: var(--wire-color-info);
--popover-soft: var(--wire-color-info-soft);
--popover-contrast: var(--wire-color-info-contrast);
.wrn-popover[data-color="info"] {
--popover-accent: var(--wrn-color-info);
--popover-soft: var(--wrn-color-info-soft);
--popover-contrast: var(--wrn-color-info-contrast);
}
.wire-popover[data-color="success"] {
--popover-accent: var(--wire-color-success);
--popover-soft: var(--wire-color-success-soft);
--popover-contrast: var(--wire-color-success-contrast);
.wrn-popover[data-color="success"] {
--popover-accent: var(--wrn-color-success);
--popover-soft: var(--wrn-color-success-soft);
--popover-contrast: var(--wrn-color-success-contrast);
}
.wire-popover[data-color="warning"] {
--popover-accent: var(--wire-color-warning);
--popover-soft: var(--wire-color-warning-soft);
--popover-contrast: var(--wire-color-warning-text);
.wrn-popover[data-color="warning"] {
--popover-accent: var(--wrn-color-warning);
--popover-soft: var(--wrn-color-warning-soft);
--popover-contrast: var(--wrn-color-warning-text);
}
.wire-popover[data-color="danger"] {
--popover-accent: var(--wire-color-danger);
--popover-soft: var(--wire-color-danger-soft);
--popover-contrast: var(--wire-color-on-danger);
.wrn-popover[data-color="danger"] {
--popover-accent: var(--wrn-color-danger);
--popover-soft: var(--wrn-color-danger-soft);
--popover-contrast: var(--wrn-color-on-danger);
}
.wire-popover__trigger {
.wrn-popover__trigger {
appearance: none;
display: inline-flex;
align-items: center;
@@ -250,9 +250,9 @@ component Popover {
gap: 0.55rem;
min-height: 2.55rem;
padding: 0.65rem 0.9rem;
color: var(--wire-color-text);
background: var(--wire-color-surface-raised);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text);
background: var(--wrn-color-surface-raised);
border: 1px solid var(--wrn-color-border);
border-radius: 0.78rem;
font: inherit;
font-size: 0.84rem;
@@ -261,33 +261,33 @@ component Popover {
transition: color 150ms ease, border-color 150ms ease, background-color 150ms ease;
}
.wire-popover__trigger:hover,
.wire-popover__trigger:focus-visible,
.wire-popover[data-open="true"] .wire-popover__trigger {
.wrn-popover__trigger:hover,
.wrn-popover__trigger:focus-visible,
.wrn-popover[data-open="true"] .wrn-popover__trigger {
color: var(--popover-accent);
background: var(--popover-soft);
border-color: color-mix(in srgb, var(--popover-accent) 38%, var(--wire-color-border));
border-color: color-mix(in srgb, var(--popover-accent) 38%, var(--wrn-color-border));
outline: none;
}
.wire-popover__trigger:disabled {
.wrn-popover__trigger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.wire-popover[data-size="sm"] .wire-popover__trigger {
.wrn-popover[data-size="sm"] .wrn-popover__trigger {
min-height: 2.2rem;
padding: 0.5rem 0.72rem;
font-size: 0.78rem;
}
.wire-popover[data-size="lg"] .wire-popover__trigger {
.wrn-popover[data-size="lg"] .wrn-popover__trigger {
min-height: 2.9rem;
padding: 0.78rem 1.05rem;
font-size: 0.9rem;
}
.wire-popover__dismiss-layer {
.wrn-popover__dismiss-layer {
position: fixed;
inset: 0;
z-index: 1120;
@@ -297,18 +297,18 @@ component Popover {
border: 0;
}
.wire-popover__panel {
.wrn-popover__panel {
position: absolute;
z-index: 1121;
top: calc(100% + 0.72rem);
left: 0;
width: 18rem;
max-width: calc(100vw - 1.5rem);
color: var(--wire-color-text);
color: var(--wrn-color-text);
background:
linear-gradient(145deg, color-mix(in srgb, var(--popover-accent) 5%, transparent), transparent 62%),
color-mix(in srgb, var(--wire-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
color-mix(in srgb, var(--wrn-color-surface-raised) 97%, transparent);
border: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wrn-color-border));
border-radius: 1rem;
box-shadow:
0 1px 0 color-mix(in srgb, white 5%, transparent) inset,
@@ -316,125 +316,125 @@ component Popover {
backdrop-filter: blur(18px);
}
.wire-popover[data-width="sm"] .wire-popover__panel {
.wrn-popover[data-width="sm"] .wrn-popover__panel {
width: 14rem;
}
.wire-popover[data-width="lg"] .wire-popover__panel {
.wrn-popover[data-width="lg"] .wrn-popover__panel {
width: 24rem;
}
.wire-popover[data-width="xl"] .wire-popover__panel {
.wrn-popover[data-width="xl"] .wrn-popover__panel {
width: 32rem;
}
.wire-popover[data-width="trigger"] .wire-popover__panel {
.wrn-popover[data-width="trigger"] .wrn-popover__panel {
width: 100%;
min-width: 100%;
}
.wire-popover[data-placement="bottom-end"] .wire-popover__panel {
.wrn-popover[data-placement="bottom-end"] .wrn-popover__panel {
right: 0;
left: auto;
}
.wire-popover[data-placement="top-start"] .wire-popover__panel {
.wrn-popover[data-placement="top-start"] .wrn-popover__panel {
top: auto;
bottom: calc(100% + 0.72rem);
}
.wire-popover[data-placement="top-end"] .wire-popover__panel {
.wrn-popover[data-placement="top-end"] .wrn-popover__panel {
top: auto;
right: 0;
bottom: calc(100% + 0.72rem);
left: auto;
}
.wire-popover[data-placement="left"] .wire-popover__panel {
.wrn-popover[data-placement="left"] .wrn-popover__panel {
top: 50%;
right: calc(100% + 0.72rem);
left: auto;
transform: translateY(-50%);
}
.wire-popover[data-placement="right"] .wire-popover__panel {
.wrn-popover[data-placement="right"] .wrn-popover__panel {
top: 50%;
left: calc(100% + 0.72rem);
transform: translateY(-50%);
}
.wire-popover[data-variant="soft"] .wire-popover__panel {
.wrn-popover[data-variant="soft"] .wrn-popover__panel {
background:
linear-gradient(145deg, var(--popover-soft), transparent 70%),
var(--wire-color-surface-raised);
var(--wrn-color-surface-raised);
box-shadow: 0 18px 48px color-mix(in srgb, black 16%, transparent);
}
.wire-popover[data-variant="outline"] .wire-popover__panel {
background: var(--wire-color-surface-raised);
.wrn-popover[data-variant="outline"] .wrn-popover__panel {
background: var(--wrn-color-surface-raised);
box-shadow: none;
}
.wire-popover[data-variant="solid"] .wire-popover__panel {
.wrn-popover[data-variant="solid"] .wrn-popover__panel {
color: var(--popover-contrast);
background: var(--popover-accent);
border-color: color-mix(in srgb, white 20%, transparent);
}
.wire-popover__arrow {
.wrn-popover__arrow {
position: absolute;
top: -0.35rem;
left: 1.25rem;
width: 0.7rem;
height: 0.7rem;
background: inherit;
border-top: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
border-left: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wire-color-border));
border-top: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wrn-color-border));
border-left: 1px solid color-mix(in srgb, var(--popover-accent) 18%, var(--wrn-color-border));
transform: rotate(45deg);
}
.wire-popover[data-placement="bottom-end"] .wire-popover__arrow {
.wrn-popover[data-placement="bottom-end"] .wrn-popover__arrow {
right: 1.25rem;
left: auto;
}
.wire-popover[data-placement="top-start"] .wire-popover__arrow,
.wire-popover[data-placement="top-end"] .wire-popover__arrow {
.wrn-popover[data-placement="top-start"] .wrn-popover__arrow,
.wrn-popover[data-placement="top-end"] .wrn-popover__arrow {
top: auto;
bottom: -0.35rem;
transform: rotate(225deg);
}
.wire-popover[data-placement="left"] .wire-popover__arrow {
.wrn-popover[data-placement="left"] .wrn-popover__arrow {
top: 50%;
right: -0.35rem;
left: auto;
transform: translateY(-50%) rotate(135deg);
}
.wire-popover[data-placement="right"] .wire-popover__arrow {
.wrn-popover[data-placement="right"] .wrn-popover__arrow {
top: 50%;
left: -0.35rem;
transform: translateY(-50%) rotate(-45deg);
}
.wire-popover__header {
.wrn-popover__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 0.8rem;
padding: 1rem 1rem 0.8rem;
border-bottom: 1px solid var(--wire-color-border);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-popover__heading {
.wrn-popover__heading {
display: flex;
align-items: flex-start;
gap: 0.7rem;
min-width: 0;
}
.wire-popover__icon {
.wrn-popover__icon {
flex: 0 0 auto;
width: 1.05rem;
height: 1.05rem;
@@ -442,33 +442,33 @@ component Popover {
color: var(--popover-accent);
}
.wire-popover[data-variant="solid"] .wire-popover__icon {
.wrn-popover[data-variant="solid"] .wrn-popover__icon {
color: currentColor;
}
.wire-popover__heading-copy {
.wrn-popover__heading-copy {
display: grid;
gap: 0.2rem;
min-width: 0;
}
.wire-popover__heading-copy h3,
.wire-popover__heading-copy p {
.wrn-popover__heading-copy h3,
.wrn-popover__heading-copy p {
margin: 0;
}
.wire-popover__heading-copy h3 {
.wrn-popover__heading-copy h3 {
font-size: 0.9rem;
font-weight: 650;
}
.wire-popover__heading-copy p {
color: var(--wire-color-text-muted);
.wrn-popover__heading-copy p {
color: var(--wrn-color-text-muted);
font-size: 0.74rem;
line-height: 1.5;
}
.wire-popover[data-variant="solid"] .wire-popover__heading-copy p {
.wrn-popover[data-variant="solid"] .wrn-popover__heading-copy p {
color: color-mix(in srgb, currentColor 76%, transparent);
}
@@ -477,7 +477,7 @@ component Popover {
* outranks the browser default and crushes the icon inside this
* fixed-size button. Same trap as Modal and Drawer.
*/
.wire-popover__close {
.wrn-popover__close {
padding: 0;
appearance: none;
display: inline-flex;
@@ -486,25 +486,25 @@ component Popover {
flex: 0 0 auto;
width: 2rem;
height: 2rem;
color: var(--wire-color-text-muted);
background: var(--wire-color-surface-soft);
border: 1px solid var(--wire-color-border);
color: var(--wrn-color-text-muted);
background: var(--wrn-color-surface-soft);
border: 1px solid var(--wrn-color-border);
border-radius: 0.65rem;
cursor: pointer;
}
.wire-popover__body {
.wrn-popover__body {
padding: 1rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.8rem;
line-height: 1.6;
}
.wire-popover__footer {
.wrn-popover__footer {
padding: 0 1rem 1rem;
}
.wire-popover__action {
.wrn-popover__action {
appearance: none;
display: inline-flex;
align-items: center;
@@ -524,7 +524,7 @@ component Popover {
}
@media (max-width: 639px) {
.wire-popover__panel {
.wrn-popover__panel {
position: fixed;
right: 0.75rem;
bottom: 0.75rem;
@@ -535,7 +535,7 @@ component Popover {
transform: none;
}
.wire-popover__arrow {
.wrn-popover__arrow {
display: none;
}
}
+611 -58
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component RangeSlider {
outputs {
input(payload: ({ value: number; name: string; sourceEvent: Event }) | ({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]))
@@ -41,57 +37,55 @@ component RangeSlider {
state currentValue = value
functions {
shared function percent() { if (Number(max) === Number(min)) { return 0 } return (Number(currentValue) - Number(min)) / (Number(max) - Number(min)) * 100 }
client function setValue(nextValue, sourceEvent, commit, detail, root, slider, input, outputElement, progressValue) { if (readonly || disabled) { return } currentValue = Number(nextValue); root = sourceEvent.currentTarget.closest(".wire-next--range-slider"); if (root) { slider = root.querySelector("[role=slider]"); input = root.querySelector("input[name='" + name + "']"); outputElement = root.querySelector("output"); progressValue = (currentValue - Number(min)) / (Number(max) - Number(min)) * 100; if (slider) { slider.setAttribute("aria-valuenow", String(currentValue)); slider.style.setProperty("--wire-range-progress", progressValue + "%") } if (input) { input.value = String(currentValue) } if (outputElement) { outputElement.textContent = String(currentValue) } } detail = { value: currentValue, min: min, max: max, step: step, name: name, sourceEvent: sourceEvent }; output.input(detail); if (commit) { output.change(detail) } }
client function setValue(nextValue, sourceEvent, commit, detail, root, slider, input, outputElement, progressValue) { if (readonly || disabled) { return } currentValue = Number(nextValue); root = sourceEvent.currentTarget.closest(".wrn-next--range-slider"); if (root) { slider = root.querySelector("[role=slider]"); input = root.querySelector("input[name='" + name + "']"); outputElement = root.querySelector("output"); progressValue = (currentValue - Number(min)) / (Number(max) - Number(min)) * 100; if (slider) { slider.setAttribute("aria-valuenow", String(currentValue)); slider.style.setProperty("--wrn-range-progress", progressValue + "%") } if (input) { input.value = String(currentValue) } if (outputElement) { outputElement.textContent = String(currentValue) } } detail = { value: currentValue, min: min, max: max, step: step, name: name, sourceEvent: sourceEvent }; output.input(detail); if (commit) { output.change(detail) } }
client function valueFromPointer(sourceEvent, rect, ratio) { rect = sourceEvent.currentTarget.getBoundingClientRect(); ratio = Math.max(0, Math.min(1, (sourceEvent.clientX - rect.left) / rect.width)); return Number(min) + ratio * (Number(max) - Number(min)) }
client function beginSlide(sourceEvent, rect, ratio, rawValue) { if (readonly || disabled) { return } sourceEvent.currentTarget.setPointerCapture(sourceEvent.pointerId); rect = sourceEvent.currentTarget.getBoundingClientRect(); ratio = Math.max(0, Math.min(1, (sourceEvent.clientX - rect.left) / rect.width)); rawValue = Number(min) + ratio * (Number(max) - Number(min)); currentValue = Number(min) + Math.round((rawValue - Number(min)) / Number(step)) * Number(step); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function moveSlide(sourceEvent, rect, ratio, rawValue) { if (!sourceEvent.currentTarget.hasPointerCapture(sourceEvent.pointerId)) { return } rect = sourceEvent.currentTarget.getBoundingClientRect(); ratio = Math.max(0, Math.min(1, (sourceEvent.clientX - rect.left) / rect.width)); rawValue = Number(min) + ratio * (Number(max) - Number(min)); currentValue = Number(min) + Math.round((rawValue - Number(min)) / Number(step)) * Number(step); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function endSlide(sourceEvent, rect, ratio, rawValue) { rect = sourceEvent.currentTarget.getBoundingClientRect(); ratio = Math.max(0, Math.min(1, (sourceEvent.clientX - rect.left) / rect.width)); rawValue = Number(min) + ratio * (Number(max) - Number(min)); currentValue = Number(min) + Math.round((rawValue - Number(min)) / Number(step)) * Number(step); if (sourceEvent.currentTarget.hasPointerCapture(sourceEvent.pointerId)) { sourceEvent.currentTarget.releasePointerCapture(sourceEvent.pointerId) } output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function selectMark(sourceEvent, root, slider, input, outputElement, nextValue, progressValue, detail) { if (readonly || disabled) { return } root = sourceEvent.currentTarget.closest(".wire-next--range-slider"); slider = root.querySelector(".wire-next__custom-slider"); input = root.querySelector(".wire-next__range-value"); outputElement = root.querySelector("output"); nextValue = Number(sourceEvent.currentTarget.dataset.value); currentValue = nextValue; progressValue = (nextValue - Number(min)) / (Number(max) - Number(min)) * 100; slider.setAttribute("aria-valuenow", String(nextValue)); slider.style.setProperty("--wire-range-progress", progressValue + "%"); input.setAttribute("value", String(nextValue)); outputElement.replaceChildren(String(nextValue)); detail = { value: nextValue, min: min, max: max, step: step, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function selectMark(sourceEvent, root, slider, input, outputElement, nextValue, progressValue, detail) { if (readonly || disabled) { return } root = sourceEvent.currentTarget.closest(".wrn-next--range-slider"); slider = root.querySelector(".wrn-next__custom-slider"); input = root.querySelector(".wrn-next__range-value"); outputElement = root.querySelector("output"); nextValue = Number(sourceEvent.currentTarget.dataset.value); currentValue = nextValue; progressValue = (nextValue - Number(min)) / (Number(max) - Number(min)) * 100; slider.setAttribute("aria-valuenow", String(nextValue)); slider.style.setProperty("--wrn-range-progress", progressValue + "%"); input.setAttribute("value", String(nextValue)); outputElement.replaceChildren(String(nextValue)); detail = { value: nextValue, min: min, max: max, step: step, name: name, sourceEvent: sourceEvent }; output.input(detail); output.change(detail) }
client function handleKey(sourceEvent) { if (sourceEvent.key === "ArrowRight") { sourceEvent.preventDefault(); currentValue = Math.min(Number(max), Number(currentValue) + Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } if (sourceEvent.key === "ArrowLeft") { sourceEvent.preventDefault(); currentValue = Math.max(Number(min), Number(currentValue) - Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } if (sourceEvent.key === "Home") { sourceEvent.preventDefault(); currentValue = Number(min); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } if (sourceEvent.key === "End") { sourceEvent.preventDefault(); currentValue = Number(max); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) } }
client function emitFocus(nameEvent, sourceEvent) { output[nameEvent]({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function decreaseValue(sourceEvent) { currentValue = Math.max(Number(min), Number(currentValue) - Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
client function increaseValue(sourceEvent) { currentValue = Math.min(Number(max), Number(currentValue) + Number(step)); output.input({ value: currentValue, name: name, sourceEvent: sourceEvent }); output.change({ value: currentValue, name: name, sourceEvent: sourceEvent }) }
}
view {
<div {...attrs} class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--field wire-next--range-slider {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wire-next__field-heading"><label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wire-next__range-control" data-variant="{variant}" data-icon-position="{iconPosition}" data-show-steps="{showSteps}">
<div {...attrs} class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--field wrn-next--range-slider {class}" data-variant="{variant}" data-inline="{inline}" data-invalid="{error ? 'true' : 'false'}">
<div class="wrn-next__field-heading"><label class="{hiddenLabel ? 'wrn-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wrn-next__field-hint">{cornerHint}</span>{/if}</div>
<div class="wrn-next__range-control" data-variant="{variant}" data-icon-position="{iconPosition}" data-show-steps="{showSteps}">
{#if icon}<span class="{icon}" aria-hidden="true"></span>{/if}
<button type="button" class="wire-next__range-stepper" aria-label="Decrease {label}" disabled="{disabled || readonly}" @click="decreaseValue(event)"></button>
<div class="wire-next__range-track">
<input id="{id || name}" class="wire-next__sr-only wire-next__range-value" type="number" name="{name}" value="{currentValue}" min="{min}" max="{max}" step="{step}" required="{required}" readonly />
<div class="wire-next__custom-slider" role="slider" tabindex="{disabled ? '-1' : '0'}" aria-label="{hiddenLabel ? label : ''}" aria-valuemin="{min}" aria-valuemax="{max}" aria-valuenow="{currentValue}" aria-disabled="{disabled}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" style="--wire-range-progress: {percent()}%" @pointerdown="beginSlide(event)" @pointermove="moveSlide(event)" @pointerup="endSlide(event)" @pointercancel="endSlide(event)" @keydown="handleKey(event)" @focus="emitFocus('focus', event)" @blur="emitFocus('blur', event)">
<span class="wire-next__slider-fill"></span><span class="wire-next__slider-thumb"></span>
{#if marks.length}<span class="wire-next__slider-marks">{#each marks as mark}<button type="button" data-value="{mark.value}" style="left: {(Number(mark.value) - Number(min)) / (Number(max) - Number(min)) * 100}%" aria-label="{mark.label}" @click="selectMark(event)"></button>{/each}</span>{/if}
<button type="button" class="wrn-next__range-stepper" aria-label="Decrease {label}" disabled="{disabled || readonly}" @click="decreaseValue(event)"></button>
<div class="wrn-next__range-track">
<input id="{id || name}" class="wrn-next__sr-only wrn-next__range-value" type="number" name="{name}" value="{currentValue}" min="{min}" max="{max}" step="{step}" required="{required}" readonly />
<div class="wrn-next__custom-slider" role="slider" tabindex="{disabled ? '-1' : '0'}" aria-label="{hiddenLabel ? label : ''}" aria-valuemin="{min}" aria-valuemax="{max}" aria-valuenow="{currentValue}" aria-disabled="{disabled}" aria-readonly="{readonly}" aria-invalid="{error ? 'true' : 'false'}" style="--wrn-range-progress: {percent()}%" @pointerdown="beginSlide(event)" @pointermove="moveSlide(event)" @pointerup="endSlide(event)" @pointercancel="endSlide(event)" @keydown="handleKey(event)" @focus="emitFocus('focus', event)" @blur="emitFocus('blur', event)">
<span class="wrn-next__slider-fill"></span><span class="wrn-next__slider-thumb"></span>
{#if marks.length}<span class="wrn-next__slider-marks">{#each marks as mark}<button type="button" data-value="{mark.value}" style="left: {(Number(mark.value) - Number(min)) / (Number(max) - Number(min)) * 100}%" aria-label="{mark.label}" @click="selectMark(event)"></button>{/each}</span>{/if}
</div>
{#if showBounds}<div class="wire-next__range-bounds"><span>{min}</span><span>Step {step}</span><span>{max}</span></div>{/if}
{#if showBounds}<div class="wrn-next__range-bounds"><span>{min}</span><span>Step {step}</span><span>{max}</span></div>{/if}
</div>
{#if showValue}<output for="{id || name}">{currentValue}</output>{/if}
<button type="button" class="wire-next__range-stepper" aria-label="Increase {label}" disabled="{disabled || readonly}" @click="increaseValue(event)">+</button>
<button type="button" class="wrn-next__range-stepper" aria-label="Increase {label}" disabled="{disabled || readonly}" @click="increaseValue(event)">+</button>
</div>
{#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}<small class="wire-next__field-error" data-error="{name}">{error}</small>
{#if helperText}<small class="wrn-next__field-help">{helperText}</small>{/if}<small class="wrn-next__field-error" data-error="{name}">{error}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next__range-track {
.wrn-next__range-track {
display: grid;
flex: 1;
min-width: 0;
gap: 0.35rem;
}
.wire-next__range-control input {
.wrn-next__range-control input {
width: 100%;
min-width: 0;
margin: 0;
flex: 1;
accent-color: var(--wire-field-color);
accent-color: var(--wrn-field-color);
}
.wire-next__custom-slider {
.wrn-next__custom-slider {
position: relative;
width: 100%;
height: 1.5rem;
@@ -99,8 +93,8 @@ component RangeSlider {
touch-action: none;
}
.wire-next__custom-slider::before,
.wire-next__slider-fill {
.wrn-next__custom-slider::before,
.wrn-next__slider-fill {
position: absolute;
top: 50%;
height: 0.4rem;
@@ -109,106 +103,665 @@ component RangeSlider {
content: "";
}
.wire-next__custom-slider::before {
.wrn-next__custom-slider::before {
right: 0;
left: 0;
background: var(--wire-color-surface-2);
box-shadow: inset 0 0 0 1px var(--wire-color-border);
background: var(--wrn-color-surface-2);
box-shadow: inset 0 0 0 1px var(--wrn-color-border);
}
.wire-next__slider-fill {
.wrn-next__slider-fill {
z-index: 1;
left: 0;
width: var(--wire-range-progress);
background: var(--wire-field-color);
width: var(--wrn-range-progress);
background: var(--wrn-field-color);
}
.wire-next__slider-thumb {
.wrn-next__slider-thumb {
position: absolute;
z-index: 3;
top: 50%;
left: var(--wire-range-progress);
left: var(--wrn-range-progress);
width: 1.25rem;
height: 1.25rem;
border: 3px solid var(--wire-field-color);
border: 3px solid var(--wrn-field-color);
border-radius: 50%;
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-1);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-1);
transform: translate(-50%, -50%);
}
.wire-next__custom-slider:focus-visible {
outline: 3px solid color-mix(in srgb, var(--wire-field-color) 28%, transparent);
.wrn-next__custom-slider:focus-visible {
outline: 3px solid color-mix(in srgb, var(--wrn-field-color) 28%, transparent);
outline-offset: 3px;
}
.wire-next__slider-marks button {
.wrn-next__slider-marks button {
position: absolute;
z-index: 2;
top: 50%;
width: 0.55rem;
height: 0.55rem;
padding: 0;
border: 2px solid var(--wire-color-surface);
border: 2px solid var(--wrn-color-surface);
border-radius: 50%;
background: var(--wire-color-muted);
background: var(--wrn-color-muted);
transform: translate(-50%, -50%);
}
.wire-next__range-control[data-show-steps="true"] input {
.wrn-next__range-control[data-show-steps="true"] input {
background-image: repeating-linear-gradient(
90deg,
transparent 0,
transparent calc(10% - 1px),
var(--wire-color-border) calc(10% - 1px),
var(--wire-color-border) 10%
var(--wrn-color-border) calc(10% - 1px),
var(--wrn-color-border) 10%
);
}
.wire-next__range-bounds {
.wrn-next__range-bounds {
display: grid;
grid-template-columns: 1fr auto 1fr;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.78em;
font-variant-numeric: tabular-nums;
}
.wire-next__range-bounds span:nth-child(2) {
.wrn-next__range-bounds span:nth-child(2) {
text-align: center;
}
.wire-next__range-bounds span:last-child {
.wrn-next__range-bounds span:last-child {
text-align: right;
}
.wire-next__range-control output {
.wrn-next__range-control output {
min-width: 3ch;
font-variant-numeric: tabular-nums;
}
.wire-next__range-stepper {
.wrn-next__range-stepper {
display: inline-grid;
width: 2.35rem;
height: 2.35rem;
flex: 0 0 auto;
place-items: center;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 50%;
background: var(--wire-color-surface);
color: var(--wire-color-text);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
font-size: 1.2rem;
cursor: pointer;
}
.wire-next__range-stepper:hover:not(:disabled) {
border-color: var(--wire-field-color);
color: var(--wire-field-color);
.wrn-next__range-stepper:hover:not(:disabled) {
border-color: var(--wrn-field-color);
color: var(--wrn-field-color);
}
.wire-next__range-stepper:disabled {
.wrn-next__range-stepper:disabled {
cursor: not-allowed;
opacity: 0.45;
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component Rating {
outputs {
input(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)
@@ -16,27 +14,95 @@ component Rating {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--rating wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--rating wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--rating {
.wrn-next--rating {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--rating > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--rating > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--rating > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--rating > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--rating > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--rating > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}
+32 -32
View File
@@ -41,25 +41,25 @@ component Scrollspy {
<nav
{...attrs}
data-ui-component="Scrollspy"
class='wire-scrollspy {class}'
class='wrn-scrollspy {class}'
data-color='{color}'
data-size='{size}'
data-wrn-scrollspy="true"
role="navigation"
aria-label='{label}'
>
<p class="wire-scrollspy__heading" data-show="heading">{heading}</p>
<p class="wrn-scrollspy__heading" data-show="heading">{heading}</p>
<ul class="wire-scrollspy__list">
<ul class="wrn-scrollspy__list">
{#each itemList() as item}
<li class="wire-scrollspy__item">
<li class="wrn-scrollspy__item">
<a
class="wire-scrollspy__link"
class="wrn-scrollspy__link"
href='{item.href || "#"}'
data-active='{isActive(item)}'
aria-current='{isActive(item) ? "location" : "false"}'
>
<span class="wire-scrollspy__label">{item.label}</span>
<span class="wrn-scrollspy__label">{item.label}</span>
</a>
</li>
{/each}
@@ -70,73 +70,73 @@ component Scrollspy {
}
style {
.wire-scrollspy {
--scrollspy-accent: var(--wire-color-primary);
.wrn-scrollspy {
--scrollspy-accent: var(--wrn-color-primary);
max-width: 100%;
}
.wire-scrollspy[data-color="secondary"] {
--scrollspy-accent: var(--wire-color-secondary);
.wrn-scrollspy[data-color="secondary"] {
--scrollspy-accent: var(--wrn-color-secondary);
}
.wire-scrollspy[data-color="success"] {
--scrollspy-accent: var(--wire-color-success);
.wrn-scrollspy[data-color="success"] {
--scrollspy-accent: var(--wrn-color-success);
}
.wire-scrollspy[data-color="danger"] {
--scrollspy-accent: var(--wire-color-danger);
.wrn-scrollspy[data-color="danger"] {
--scrollspy-accent: var(--wrn-color-danger);
}
.wire-scrollspy[data-color="info"] {
--scrollspy-accent: var(--wire-color-info);
.wrn-scrollspy[data-color="info"] {
--scrollspy-accent: var(--wrn-color-info);
}
.wire-scrollspy[data-size="sm"] {
.wrn-scrollspy[data-size="sm"] {
font-size: 0.82rem;
}
.wire-scrollspy[data-size="lg"] {
.wrn-scrollspy[data-size="lg"] {
font-size: 1rem;
}
.wire-scrollspy__heading {
.wrn-scrollspy__heading {
margin: 0 0 0.5rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.wire-scrollspy__list {
.wrn-scrollspy__list {
display: grid;
gap: 0.1rem;
margin: 0;
padding: 0;
list-style: none;
border-left: 1px solid var(--wire-color-border);
border-left: 1px solid var(--wrn-color-border);
}
.wire-scrollspy__link {
.wrn-scrollspy__link {
display: block;
padding: 0.3rem 0.75rem;
margin-left: -1px;
border-left: 2px solid transparent;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.85rem;
text-decoration: none;
}
.wire-scrollspy__link:hover {
color: var(--wire-color-text);
.wrn-scrollspy__link:hover {
color: var(--wrn-color-text);
}
.wire-scrollspy__link:focus-visible {
.wrn-scrollspy__link:focus-visible {
outline: 2px solid var(--scrollspy-accent);
outline-offset: -2px;
}
.wire-scrollspy__link[data-active="true"] {
.wrn-scrollspy__link[data-active="true"] {
border-left-color: var(--scrollspy-accent);
color: var(--scrollspy-accent);
font-weight: 600;
@@ -148,22 +148,22 @@ component Scrollspy {
* line rather than a screenful.
*/
@media (max-width: 767px) {
.wire-scrollspy__list {
.wrn-scrollspy__list {
grid-auto-flow: column;
grid-auto-columns: max-content;
overflow-x: auto;
border-left: 0;
border-bottom: 1px solid var(--wire-color-border);
border-bottom: 1px solid var(--wrn-color-border);
}
.wire-scrollspy__link {
.wrn-scrollspy__link {
margin-left: 0;
border-left: 0;
border-bottom: 2px solid transparent;
white-space: nowrap;
}
.wire-scrollspy__link[data-active="true"] {
.wrn-scrollspy__link[data-active="true"] {
border-left-color: transparent;
border-bottom-color: var(--scrollspy-accent);
}
+39 -39
View File
@@ -26,19 +26,19 @@ component SearchBox {
<form
data-ui-component="SearchBox"
role="search"
class='wire-search-box {class}'
class='wrn-search-box {class}'
@submit='event.preventDefault(); output.search({ value: query, name: name })'
>
<label
class="wire-search-box__label"
class="wrn-search-box__label"
data-hidden='{label === "" ? "true" : "false"}'
>
{label}
</label>
<div class="wire-search-box__control">
<div class="wrn-search-box__control">
<span
class="icon-[lucide--search] wire-search-box__search-icon"
class="icon-[lucide--search] wrn-search-box__search-icon"
aria-hidden="true"
></span>
@@ -53,7 +53,7 @@ component SearchBox {
disabled='{disabled}'
required='{required}'
autocomplete="off"
class="wire-search-box__input"
class="wrn-search-box__input"
data-size='{size}'
@input='query = event.target.value'
@change='query = event.target.value'
@@ -63,34 +63,34 @@ component SearchBox {
type="button"
aria-label="Clear search"
data-show='query.length > 0 && !disabled'
class="wire-search-box__button wire-search-box__clear"
class="wrn-search-box__button wrn-search-box__clear"
@click='query = ""; event.currentTarget.parentElement.querySelector("input")?.focus(); output.clear({ value: "", name: name })'
>
<span class="icon-[lucide--x] wire-search-box__button-icon" aria-hidden="true"></span>
<span class="icon-[lucide--x] wrn-search-box__button-icon" aria-hidden="true"></span>
</button>
<button
type="submit"
aria-label='{label || "Search"}'
disabled='{disabled}'
class="wire-search-box__button wire-search-box__submit"
class="wrn-search-box__button wrn-search-box__submit"
>
<span class="icon-[lucide--arrow-right] wire-search-box__button-icon" aria-hidden="true"></span>
<span class="icon-[lucide--arrow-right] wrn-search-box__button-icon" aria-hidden="true"></span>
</button>
</div>
</form>
}
style {
.wire-search-box { width: 100%; }
.wire-search-box__label {
.wrn-search-box { width: 100%; }
.wrn-search-box__label {
display: block;
margin-bottom: 0.5rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
font-size: 0.875rem;
font-weight: 600;
}
.wire-search-box__label[data-hidden="true"] {
.wrn-search-box__label[data-hidden="true"] {
position: absolute;
width: 1px;
height: 1px;
@@ -101,50 +101,50 @@ component SearchBox {
white-space: nowrap;
border: 0;
}
.wire-search-box__control { position: relative; display: flex; align-items: center; }
.wire-search-box__search-icon {
.wrn-search-box__control { position: relative; display: flex; align-items: center; }
.wrn-search-box__search-icon {
position: absolute;
left: 1rem;
width: 1.25rem;
height: 1.25rem;
color: var(--wire-color-input-placeholder);
color: var(--wrn-color-input-placeholder);
pointer-events: none;
}
.wire-search-box__input {
.wrn-search-box__input {
width: 100%;
height: 3rem;
padding: 0 6rem 0 3rem;
border: 1px solid var(--wire-color-input-border);
border-radius: var(--wire-radius);
border: 1px solid var(--wrn-color-input-border);
border-radius: var(--wrn-radius);
outline: none;
color: var(--wire-color-input-text);
background: var(--wire-color-input-background);
transition: border-color var(--wire-motion-base) var(--wire-ease-standard), box-shadow var(--wire-motion-base) var(--wire-ease-standard);
color: var(--wrn-color-input-text);
background: var(--wrn-color-input-background);
transition: border-color var(--wrn-motion-base) var(--wrn-ease-standard), box-shadow var(--wrn-motion-base) var(--wrn-ease-standard);
}
.wire-search-box__input[data-size="sm"] { height: 2.5rem; }
.wire-search-box__input[data-size="lg"] { height: 3.5rem; }
.wire-search-box__input::placeholder { color: var(--wire-color-input-placeholder); }
.wire-search-box__input:hover { border-color: var(--wire-color-input-border-hover); }
.wire-search-box__input:focus {
border-color: var(--wire-color-input-border-focus);
box-shadow: 0 0 0 2px var(--wire-color-focus);
.wrn-search-box__input[data-size="sm"] { height: 2.5rem; }
.wrn-search-box__input[data-size="lg"] { height: 3.5rem; }
.wrn-search-box__input::placeholder { color: var(--wrn-color-input-placeholder); }
.wrn-search-box__input:hover { border-color: var(--wrn-color-input-border-hover); }
.wrn-search-box__input:focus {
border-color: var(--wrn-color-input-border-focus);
box-shadow: 0 0 0 2px var(--wrn-color-focus);
}
.wire-search-box__input:disabled, .wire-search-box__button:disabled { cursor: not-allowed; opacity: 0.6; }
.wire-search-box__button {
.wrn-search-box__input:disabled, .wrn-search-box__button:disabled { cursor: not-allowed; opacity: 0.6; }
.wrn-search-box__button {
position: absolute;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
border: 0;
border-radius: var(--wire-radius-sm);
transition: color var(--wire-motion-base), background var(--wire-motion-base);
border-radius: var(--wrn-radius-sm);
transition: color var(--wrn-motion-base), background var(--wrn-motion-base);
}
.wire-search-box__clear { right: 3rem; width: 2rem; height: 2rem; color: var(--wire-color-text-muted); background: transparent; }
.wire-search-box__clear:hover { color: var(--wire-color-text); background: var(--wire-color-surface-soft); }
.wire-search-box__submit { right: 0.5rem; width: 2.25rem; height: 2.25rem; color: var(--wire-color-on-primary); background: var(--wire-color-primary); }
.wire-search-box__submit:hover { background: var(--wire-color-primary-hover); }
.wire-search-box__button:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: 2px; }
.wire-search-box__button-icon { width: 1rem; height: 1rem; }
.wrn-search-box__clear { right: 3rem; width: 2rem; height: 2rem; color: var(--wrn-color-text-muted); background: transparent; }
.wrn-search-box__clear:hover { color: var(--wrn-color-text); background: var(--wrn-color-surface-soft); }
.wrn-search-box__submit { right: 0.5rem; width: 2.25rem; height: 2.25rem; color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); }
.wrn-search-box__submit:hover { background: var(--wrn-color-primary-hover); }
.wrn-search-box__button:focus-visible { outline: 2px solid var(--wrn-color-focus); outline-offset: 2px; }
.wrn-search-box__button-icon { width: 1rem; height: 1rem; }
}
}
+61 -61
View File
@@ -88,29 +88,29 @@ component Sidebar {
<div
{...attrs}
data-ui-component="Sidebar"
class='wire-sidebar {class}'
class='wrn-sidebar {class}'
data-color='{color}'
data-size='{size}'
>
<button
type="button"
class="wire-sidebar__launcher"
class="wrn-sidebar__launcher"
aria-label='{mobileLabel}'
aria-expanded='{drawerOpen}'
@click='openDrawer(event)'
>
<span class="wire-sidebar__launcher-bar" aria-hidden="true"></span>
<span class="wire-sidebar__launcher-text">{mobileLabel}</span>
<span class="wrn-sidebar__launcher-bar" aria-hidden="true"></span>
<span class="wrn-sidebar__launcher-text">{mobileLabel}</span>
</button>
<nav class="wire-sidebar__rail" aria-label='{label}'>
<ul class="wire-sidebar__list" data-wrn-roving="vertical">
<nav class="wrn-sidebar__rail" aria-label='{label}'>
<ul class="wrn-sidebar__list" data-wrn-roving="vertical">
{#each entryList() as entry}
<li class="wire-sidebar__entry" data-group='{isGroup(entry)}'>
<p class="wire-sidebar__heading" data-show="entry.heading">{entry.heading}</p>
<li class="wrn-sidebar__entry" data-group='{isGroup(entry)}'>
<p class="wrn-sidebar__heading" data-show="entry.heading">{entry.heading}</p>
<a
class="wire-sidebar__link"
class="wrn-sidebar__link"
data-show="!entry.heading"
href='{entry.href || "#"}'
data-wrn-roving-item="true"
@@ -120,19 +120,19 @@ component Sidebar {
@click='choose(entry, 1)'
>
<span
class='wire-sidebar__icon {entry.icon}'
class='wrn-sidebar__icon {entry.icon}'
data-show="entry.icon"
aria-hidden="true"
></span>
<span class="wire-sidebar__label">{entry.label}</span>
<span class="wire-sidebar__badge" data-show="entry.badge">{entry.badge}</span>
<span class="wrn-sidebar__label">{entry.label}</span>
<span class="wrn-sidebar__badge" data-show="entry.badge">{entry.badge}</span>
</a>
<ul class="wire-sidebar__sublist" data-show="childrenOf(entry).length > 0">
<ul class="wrn-sidebar__sublist" data-show="childrenOf(entry).length > 0">
{#each childrenOf(entry) as child}
<li class="wire-sidebar__entry">
<li class="wrn-sidebar__entry">
<a
class="wire-sidebar__link"
class="wrn-sidebar__link"
href='{child.href || "#"}'
data-active='{isActive(child)}'
aria-current='{isActive(child) ? "page" : "false"}'
@@ -140,28 +140,28 @@ component Sidebar {
@click='choose(child, 2)'
>
<span
class='wire-sidebar__icon {child.icon}'
class='wrn-sidebar__icon {child.icon}'
data-show="child.icon"
aria-hidden="true"
></span>
<span class="wire-sidebar__label">{child.label}</span>
<span class="wire-sidebar__badge" data-show="child.badge">{child.badge}</span>
<span class="wrn-sidebar__label">{child.label}</span>
<span class="wrn-sidebar__badge" data-show="child.badge">{child.badge}</span>
</a>
<ul
class="wire-sidebar__sublist wire-sidebar__sublist--level3"
class="wrn-sidebar__sublist wrn-sidebar__sublist--level3"
data-show="childrenOf(child).length > 0"
>
{#each childrenOf(child) as leaf}
<li class="wire-sidebar__entry">
<li class="wrn-sidebar__entry">
<a
class="wire-sidebar__link"
class="wrn-sidebar__link"
href='{leaf.href || "#"}'
data-active='{isActive(leaf)}'
aria-current='{isActive(leaf) ? "page" : "false"}'
@click='choose(leaf, 3)'
>
<span class="wire-sidebar__label">{leaf.label}</span>
<span class="wrn-sidebar__label">{leaf.label}</span>
</a>
</li>
{/each}
@@ -188,41 +188,41 @@ component Sidebar {
}
style {
.wire-sidebar {
--sidebar-accent: var(--wire-color-primary);
.wrn-sidebar {
--sidebar-accent: var(--wrn-color-primary);
max-width: 100%;
}
.wire-sidebar[data-color="secondary"] {
--sidebar-accent: var(--wire-color-secondary);
.wrn-sidebar[data-color="secondary"] {
--sidebar-accent: var(--wrn-color-secondary);
}
.wire-sidebar[data-color="success"] {
--sidebar-accent: var(--wire-color-success);
.wrn-sidebar[data-color="success"] {
--sidebar-accent: var(--wrn-color-success);
}
.wire-sidebar[data-color="danger"] {
--sidebar-accent: var(--wire-color-danger);
.wrn-sidebar[data-color="danger"] {
--sidebar-accent: var(--wrn-color-danger);
}
.wire-sidebar[data-color="info"] {
--sidebar-accent: var(--wire-color-info);
.wrn-sidebar[data-color="info"] {
--sidebar-accent: var(--wrn-color-info);
}
.wire-sidebar[data-size="sm"] {
.wrn-sidebar[data-size="sm"] {
font-size: 0.82rem;
}
.wire-sidebar[data-size="lg"] {
.wrn-sidebar[data-size="lg"] {
font-size: 1rem;
}
.wire-sidebar__rail {
.wrn-sidebar__rail {
width: 100%;
}
.wire-sidebar__list,
.wire-sidebar__sublist {
.wrn-sidebar__list,
.wrn-sidebar__sublist {
display: grid;
gap: 0.1rem;
margin: 0;
@@ -230,62 +230,62 @@ component Sidebar {
list-style: none;
}
.wire-sidebar__sublist {
.wrn-sidebar__sublist {
margin-left: 0.85rem;
padding-left: 0.5rem;
border-left: 1px solid var(--wire-color-border);
border-left: 1px solid var(--wrn-color-border);
}
.wire-sidebar__entry[data-group="true"] + .wire-sidebar__entry {
.wrn-sidebar__entry[data-group="true"] + .wrn-sidebar__entry {
margin-top: 0.35rem;
}
.wire-sidebar__heading {
.wrn-sidebar__heading {
margin: 0.9rem 0 0.35rem;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.wire-sidebar__link {
.wrn-sidebar__link {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.45rem 0.6rem;
border-radius: var(--wire-radius-sm);
color: var(--wire-color-text-muted);
border-radius: var(--wrn-radius-sm);
color: var(--wrn-color-text-muted);
font-size: 0.88rem;
font-weight: 600;
text-decoration: none;
}
.wire-sidebar__link:hover {
background: var(--wire-color-surface-soft);
color: var(--wire-color-text);
.wrn-sidebar__link:hover {
background: var(--wrn-color-surface-soft);
color: var(--wrn-color-text);
}
.wire-sidebar__link:focus-visible {
.wrn-sidebar__link:focus-visible {
outline: 2px solid var(--sidebar-accent);
outline-offset: 2px;
}
.wire-sidebar__link[data-active="true"] {
.wrn-sidebar__link[data-active="true"] {
background: color-mix(in srgb, var(--sidebar-accent) 16%, transparent);
color: var(--sidebar-accent);
}
.wire-sidebar__link[aria-disabled="true"] {
.wrn-sidebar__link[aria-disabled="true"] {
opacity: 0.5;
pointer-events: none;
}
.wire-sidebar__label {
.wrn-sidebar__label {
min-width: 0;
}
.wire-sidebar__badge {
.wrn-sidebar__badge {
margin-left: auto;
padding: 0.05rem 0.4rem;
border-radius: 999px;
@@ -294,21 +294,21 @@ component Sidebar {
font-size: 0.72rem;
}
.wire-sidebar__launcher {
.wrn-sidebar__launcher {
display: none;
align-items: center;
gap: 0.5rem;
padding: 0.45rem 0.7rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
font-size: 0.9rem;
cursor: pointer;
}
.wire-sidebar__launcher-bar {
.wrn-sidebar__launcher-bar {
width: 1rem;
height: 2px;
background: currentColor;
@@ -323,11 +323,11 @@ component Sidebar {
* the focus trap and the scroll lock.
*/
@media (max-width: 767px) {
.wire-sidebar__launcher {
.wrn-sidebar__launcher {
display: inline-flex;
}
.wire-sidebar__rail {
.wrn-sidebar__rail {
display: none;
}
}
+79 -79
View File
@@ -134,74 +134,74 @@ component Stepper {
<div
{...attrs}
data-ui-component="Stepper"
class='wire-stepper {class}'
class='wrn-stepper {class}'
data-orientation='{orientation}'
data-color='{color}'
data-size='{size}'
data-clickable='{clickable}'
>
<ol
class="wire-stepper__list"
class="wrn-stepper__list"
data-wrn-roving='{rovingAxis()}'
aria-label='{label}'
>
{#each stepList() as step, index}
<li
class="wire-stepper__step"
class="wrn-stepper__step"
data-status='{statusFor(index)}'
aria-current='{statusFor(index) === "current" ? "step" : "false"}'
>
<button
type="button"
class="wire-stepper__button"
class="wrn-stepper__button"
data-wrn-roving-item='{clickable ? "true" : "false"}'
@click='selectStep(index, step)'
>
<span class="wire-stepper__marker" aria-hidden="true">
<span class='wire-stepper__icon {step.icon}' data-show="step.icon"></span>
<span class="wire-stepper__number" data-show="!step.icon">{index + 1}</span>
<span class="wrn-stepper__marker" aria-hidden="true">
<span class='wrn-stepper__icon {step.icon}' data-show="step.icon"></span>
<span class="wrn-stepper__number" data-show="!step.icon">{index + 1}</span>
</span>
<span class="wire-stepper__body">
<span class="wire-stepper__label">{step.label}</span>
<span class="wire-stepper__description" data-show="step.description">
<span class="wrn-stepper__body">
<span class="wrn-stepper__label">{step.label}</span>
<span class="wrn-stepper__description" data-show="step.description">
{step.description}
</span>
</span>
</button>
<span class="wire-stepper__custom">
<span class="wrn-stepper__custom">
<slot name="step-{index}"></slot>
</span>
</li>
{/each}
</ol>
<div class="wire-stepper__panels" data-show="showPanel">
<div class="wrn-stepper__panels" data-show="showPanel">
{#each stepList() as step, index}
<div
class="wire-stepper__panel"
class="wrn-stepper__panel"
data-show='isActiveStep(index)'
aria-hidden='{index === activeIndex() ? "false" : "true"}'
>
<h4 class="wire-stepper__panel-title" data-show="step.title">{step.title}</h4>
<p class="wire-stepper__panel-body" data-show="step.content">{step.content}</p>
<h4 class="wrn-stepper__panel-title" data-show="step.title">{step.title}</h4>
<p class="wrn-stepper__panel-body" data-show="step.content">{step.content}</p>
<slot name="panel-{index}"></slot>
</div>
{/each}
</div>
<div class="wire-stepper__controls" data-show="controls">
<div class="wrn-stepper__controls" data-show="controls">
<button
type="button"
class="wire-stepper__button-control wire-stepper__back"
class="wrn-stepper__button-control wrn-stepper__back"
disabled='{activeIndex() === 0}'
@click='goBack()'
>
{backLabel}
</button>
<span class="wire-stepper__controls-spacer"></span>
<span class="wrn-stepper__controls-spacer"></span>
<button
type="button"
class="wire-stepper__button-control wire-stepper__skip"
class="wrn-stepper__button-control wrn-stepper__skip"
data-show="allowSkip && !onLastStep()"
@click='goSkip()'
>
@@ -209,7 +209,7 @@ component Stepper {
</button>
<button
type="button"
class="wire-stepper__button-control wire-stepper__next"
class="wrn-stepper__button-control wrn-stepper__next"
data-primary="true"
disabled='{nextDisabled}'
@click='goNext()'
@@ -223,12 +223,12 @@ component Stepper {
}
style {
.wire-stepper {
--stepper-accent: var(--wire-color-primary);
.wrn-stepper {
--stepper-accent: var(--wrn-color-primary);
max-width: 100%;
}
.wire-stepper__list {
.wrn-stepper__list {
display: flex;
gap: 0.5rem;
margin: 0;
@@ -237,35 +237,35 @@ component Stepper {
max-width: 100%;
}
.wire-stepper[data-color="secondary"] {
--stepper-accent: var(--wire-color-secondary);
.wrn-stepper[data-color="secondary"] {
--stepper-accent: var(--wrn-color-secondary);
}
.wire-stepper[data-color="success"] {
--stepper-accent: var(--wire-color-success);
.wrn-stepper[data-color="success"] {
--stepper-accent: var(--wrn-color-success);
}
.wire-stepper[data-color="danger"] {
--stepper-accent: var(--wire-color-danger);
.wrn-stepper[data-color="danger"] {
--stepper-accent: var(--wrn-color-danger);
}
.wire-stepper[data-color="info"] {
--stepper-accent: var(--wire-color-info);
.wrn-stepper[data-color="info"] {
--stepper-accent: var(--wrn-color-info);
}
.wire-stepper[data-size="sm"] {
.wrn-stepper[data-size="sm"] {
font-size: 0.82rem;
}
.wire-stepper[data-size="lg"] {
.wrn-stepper[data-size="lg"] {
font-size: 1rem;
}
.wire-stepper[data-orientation="vertical"] .wire-stepper__list {
.wrn-stepper[data-orientation="vertical"] .wrn-stepper__list {
flex-direction: column;
}
.wire-stepper__step {
.wrn-stepper__step {
display: flex;
flex-direction: column;
flex: 1 1 0;
@@ -273,7 +273,7 @@ component Stepper {
gap: 0.35rem;
}
.wire-stepper__button {
.wrn-stepper__button {
appearance: none;
display: flex;
align-items: center;
@@ -281,7 +281,7 @@ component Stepper {
width: 100%;
padding: 0.5rem;
border: 0;
border-radius: var(--wire-radius-sm);
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
font: inherit;
@@ -289,139 +289,139 @@ component Stepper {
cursor: default;
}
.wire-stepper[data-clickable="true"] .wire-stepper__button {
.wrn-stepper[data-clickable="true"] .wrn-stepper__button {
cursor: pointer;
}
.wire-stepper[data-clickable="true"] .wire-stepper__button:hover {
background: var(--wire-color-surface-soft);
.wrn-stepper[data-clickable="true"] .wrn-stepper__button:hover {
background: var(--wrn-color-surface-soft);
}
.wire-stepper__button:focus-visible {
.wrn-stepper__button:focus-visible {
outline: 2px solid var(--stepper-accent);
outline-offset: 2px;
}
.wire-stepper__marker {
.wrn-stepper__marker {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2rem;
height: 2rem;
border: 1px solid var(--wire-color-border);
border: 1px solid var(--wrn-color-border);
border-radius: 999px;
background: var(--wire-color-surface);
background: var(--wrn-color-surface);
font-size: 0.85rem;
font-weight: 700;
}
.wire-stepper__step[data-status="complete"] .wire-stepper__marker {
.wrn-stepper__step[data-status="complete"] .wrn-stepper__marker {
border-color: var(--stepper-accent);
background: var(--stepper-accent);
color: var(--wire-color-primary-contrast);
color: var(--wrn-color-primary-contrast);
}
.wire-stepper__step[data-status="current"] .wire-stepper__marker {
.wrn-stepper__step[data-status="current"] .wrn-stepper__marker {
border-color: var(--stepper-accent);
color: var(--stepper-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--stepper-accent) 22%, transparent);
}
.wire-stepper__step[data-status="upcoming"] .wire-stepper__marker {
color: var(--wire-color-text-muted);
.wrn-stepper__step[data-status="upcoming"] .wrn-stepper__marker {
color: var(--wrn-color-text-muted);
}
.wire-stepper__body {
.wrn-stepper__body {
display: flex;
flex-direction: column;
min-width: 0;
}
.wire-stepper__label {
.wrn-stepper__label {
font-size: 0.9rem;
font-weight: 600;
}
.wire-stepper__description {
color: var(--wire-color-text-muted);
.wrn-stepper__description {
color: var(--wrn-color-text-muted);
font-size: 0.78rem;
}
.wire-stepper__step[data-status="upcoming"] .wire-stepper__label {
color: var(--wire-color-text-muted);
.wrn-stepper__step[data-status="upcoming"] .wrn-stepper__label {
color: var(--wrn-color-text-muted);
}
.wire-stepper__panels {
.wrn-stepper__panels {
margin-top: 1rem;
}
.wire-stepper__panel {
.wrn-stepper__panel {
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-md);
background: var(--wire-color-surface);
animation: wire-stepper-in 200ms ease both;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
animation: wrn-stepper-in 200ms ease both;
}
.wire-stepper__panel-title {
.wrn-stepper__panel-title {
margin: 0 0 0.35rem;
font-size: 0.95rem;
font-weight: 700;
}
.wire-stepper__panel-body {
.wrn-stepper__panel-body {
margin: 0;
color: var(--wire-color-text-muted);
color: var(--wrn-color-text-muted);
line-height: 1.6;
}
.wire-stepper__controls {
.wrn-stepper__controls {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.wire-stepper__controls-spacer {
.wrn-stepper__controls-spacer {
flex: 1 1 auto;
}
.wire-stepper__button-control {
.wrn-stepper__button-control {
appearance: none;
padding: 0.45rem 0.9rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
background: var(--wire-color-surface);
color: var(--wire-color-text);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
font-size: 0.86rem;
font-weight: 600;
cursor: pointer;
}
.wire-stepper__button-control:hover:not(:disabled) {
background: var(--wire-color-surface-soft);
.wrn-stepper__button-control:hover:not(:disabled) {
background: var(--wrn-color-surface-soft);
}
.wire-stepper__button-control:focus-visible {
.wrn-stepper__button-control:focus-visible {
outline: 2px solid var(--stepper-accent);
outline-offset: 2px;
}
.wire-stepper__button-control[data-primary="true"] {
.wrn-stepper__button-control[data-primary="true"] {
border-color: var(--stepper-accent);
background: var(--stepper-accent);
color: var(--wire-color-primary-contrast);
color: var(--wrn-color-primary-contrast);
}
/* A held step has to look held, or the button reads as broken. */
.wire-stepper__button-control:disabled {
.wrn-stepper__button-control:disabled {
opacity: 0.45;
cursor: not-allowed;
}
@keyframes wire-stepper-in {
@keyframes wrn-stepper-in {
from {
opacity: 0;
transform: translateX(0.75rem);
@@ -434,7 +434,7 @@ component Stepper {
/* A horizontal stepper cannot stay side by side on a phone. */
@media (max-width: 639px) {
.wire-stepper__list {
.wrn-stepper__list {
flex-direction: column;
}
}
+637 -75
View File
@@ -1,7 +1,3 @@
import FieldStyles from "../styles/FieldStyles.wrn"
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component StrongPassword {
outputs {
input(payload: { value: string; score: number; maximumScore: number; percent: number; level: string })
@@ -121,13 +117,13 @@ component StrongPassword {
view {
<div
class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--strong-password wire-next--strong-password-{presentation} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--strong-password wrn-next--strong-password-{presentation} {invalid ? 'wrn-next--invalid' : ''} {disabled ? 'wrn-next--disabled' : ''} {class}"
data-wrn-strong-password
data-strength="{password ? strengthLevel() : 'empty'}"
data-score="{password ? score() : 0}"
>
<label for="{name}-strong-password">{label}</label>
<div class="wire-next__strong-password-anchor">
<div class="wrn-next__strong-password-anchor">
<input
{...attrs}
id="{name}-strong-password"
@@ -150,18 +146,18 @@ component StrongPassword {
{#if presentation === "popover"}
<div
class="wire-next__strong-password-popover"
class="wrn-next__strong-password-popover"
data-open="{detailsOpen ? 'true' : 'false'}"
role="status"
>
<div class="wire-next__strong-password-popover-arrow" aria-hidden="true"></div>
<div class="wire-next__strong-password-details">
<div class="wire-next__strong-password-summary">
<div class="wrn-next__strong-password-popover-arrow" aria-hidden="true"></div>
<div class="wrn-next__strong-password-details">
<div class="wrn-next__strong-password-summary">
<span>Password strength</span>
<strong>{password ? strengthLabel() : emptyLabel}</strong>
</div>
<div
class="wire-next__strong-password-meter"
class="wrn-next__strong-password-meter"
role="progressbar"
aria-label="Password strength"
aria-valuemin="0"
@@ -171,14 +167,14 @@ component StrongPassword {
<span style="width: {password ? strengthPercent() : 0}%"></span>
</div>
{#if showRequirements}
<ul class="wire-next__strong-password-requirements">
<ul class="wrn-next__strong-password-requirements">
<li data-met="{password && hasMinimumLength() ? 'true' : 'false'}">
<span aria-hidden="true"></span>At least {minLength} characters
</li>
{#if requireLowercase}<li data-met="{password && hasLowercase() ? 'true' : 'false'}"><span aria-hidden="true"></span>One lowercase letter</li>{/if}
{#if requireUppercase}<li data-met="{password && hasUppercase() ? 'true' : 'false'}"><span aria-hidden="true"></span>One uppercase letter</li>{/if}
{#if requireNumber}<li data-met="{password && hasNumber() ? 'true' : 'false'}"><span aria-hidden="true"></span>One number</li>{/if}
{#if requireSpecialCharacter}<li data-met="{password && hasSpecialCharacter() ? 'true' : 'false'}"><span aria-hidden="true"></span>One special character: {specialCharactersSet}</li>{/if}
{#if requireSpecialCharacter}<li class="wrn-next__strong-password-special" data-met="{password && hasSpecialCharacter() ? 'true' : 'false'}"><span aria-hidden="true"></span>One special character: {specialCharactersSet}</li>{/if}
</ul>
{/if}
{#if hintText}<small id="{name}-hint">{hintText}</small>{/if}
@@ -188,13 +184,13 @@ component StrongPassword {
</div>
{#if presentation !== "popover"}
<div class="wire-next__strong-password-details">
<div class="wire-next__strong-password-summary">
<div class="wrn-next__strong-password-details">
<div class="wrn-next__strong-password-summary">
<span>Password strength</span>
<strong>{password ? strengthLabel() : emptyLabel}</strong>
</div>
<div
class="wire-next__strong-password-meter"
class="wrn-next__strong-password-meter"
role="progressbar"
aria-label="Password strength"
aria-valuemin="0"
@@ -204,112 +200,110 @@ component StrongPassword {
<span style="width: {password ? strengthPercent() : 0}%"></span>
</div>
{#if showRequirements}
<ul class="wire-next__strong-password-requirements">
<ul class="wrn-next__strong-password-requirements">
<li data-met="{password && hasMinimumLength() ? 'true' : 'false'}"><span aria-hidden="true"></span>At least {minLength} characters</li>
{#if requireLowercase}<li data-met="{password && hasLowercase() ? 'true' : 'false'}"><span aria-hidden="true"></span>One lowercase letter</li>{/if}
{#if requireUppercase}<li data-met="{password && hasUppercase() ? 'true' : 'false'}"><span aria-hidden="true"></span>One uppercase letter</li>{/if}
{#if requireNumber}<li data-met="{password && hasNumber() ? 'true' : 'false'}"><span aria-hidden="true"></span>One number</li>{/if}
{#if requireSpecialCharacter}<li data-met="{password && hasSpecialCharacter() ? 'true' : 'false'}"><span aria-hidden="true"></span>One special character: {specialCharactersSet}</li>{/if}
{#if requireSpecialCharacter}<li class="wrn-next__strong-password-special" data-met="{password && hasSpecialCharacter() ? 'true' : 'false'}"><span aria-hidden="true"></span>One special character: {specialCharactersSet}</li>{/if}
</ul>
{/if}
{#if hintText}<small id="{name}-hint">{hintText}</small>{/if}
</div>
{/if}
<small id="{name}-validation" class="wire-next__validation" data-error="{name}">{validationMessage}</small>
<small id="{name}-validation" class="wrn-next__validation" data-error="{name}">{validationMessage}</small>
</div>
<FieldStyles hidden />
<ComponentBaseStyles hidden />
}
style {
.wire-next--strong-password {
.wrn-next--strong-password {
position: relative;
display: grid;
width: 100%;
gap: 0.6rem;
color: var(--wire-color-text);
color: var(--wrn-color-text);
}
.wire-next--strong-password > label {
.wrn-next--strong-password > label {
font-size: 0.82em;
font-weight: 700;
}
.wire-next__strong-password-anchor {
.wrn-next__strong-password-anchor {
position: relative;
}
.wire-next--strong-password input {
.wrn-next--strong-password input {
width: 100%;
min-height: 2.75em;
padding: 0.68em 0.85em;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius-sm);
min-height: 2.65rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
outline: 0;
color: var(--wire-color-text);
background: var(--wire-color-surface);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
font: inherit;
transition:
border-color 160ms ease,
box-shadow 160ms ease;
}
.wire-next--strong-password input:focus-visible {
border-color: var(--wire-component-color);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wire-component-color) 16%, transparent);
.wrn-next--strong-password input:focus-visible {
border-color: var(--wrn-component-color);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-component-color) 16%, transparent);
}
.wire-next__strong-password-details {
.wrn-next__strong-password-details {
display: grid;
gap: 0.65rem;
}
.wire-next__strong-password-summary {
.wrn-next__strong-password-summary {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.75em;
}
.wire-next__strong-password-summary strong {
color: var(--wire-color-text);
.wrn-next__strong-password-summary strong {
color: var(--wrn-color-text);
}
.wire-next__strong-password-meter {
.wrn-next__strong-password-meter {
width: 100%;
height: 0.42rem;
overflow: hidden;
border-radius: 999px;
background: color-mix(in srgb, var(--wire-color-border) 72%, transparent);
background: color-mix(in srgb, var(--wrn-color-border) 72%, transparent);
}
.wire-next__strong-password-meter > span {
.wrn-next__strong-password-meter > span {
display: block;
width: 0;
height: 100%;
border-radius: inherit;
background: var(--wire-color-danger);
background: var(--wrn-color-danger);
transition:
width 180ms ease,
background-color 180ms ease;
}
.wire-next--strong-password[data-strength="fair"] .wire-next__strong-password-meter > span {
background: var(--wire-color-warning);
.wrn-next--strong-password[data-strength="fair"] .wrn-next__strong-password-meter > span {
background: var(--wrn-color-warning);
}
.wire-next--strong-password[data-strength="good"] .wire-next__strong-password-meter > span {
background: var(--wire-component-color);
.wrn-next--strong-password[data-strength="good"] .wrn-next__strong-password-meter > span {
background: var(--wrn-component-color);
}
.wire-next--strong-password[data-strength="strong"] .wire-next__strong-password-meter > span {
background: var(--wire-color-success);
.wrn-next--strong-password[data-strength="strong"] .wrn-next__strong-password-meter > span {
background: var(--wrn-color-success);
}
.wire-next__strong-password-requirements {
.wrn-next__strong-password-requirements {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.45rem 1rem;
@@ -318,17 +312,26 @@ component StrongPassword {
list-style: none;
}
.wire-next__strong-password-requirements li {
.wrn-next__strong-password-requirements li {
display: flex;
min-width: 0;
align-items: flex-start;
gap: 0.45rem;
color: var(--wire-color-muted);
color: var(--wrn-color-muted);
font-size: 0.72em;
line-height: 1.45;
}
.wire-next__strong-password-requirements li > span {
.wrn-next__strong-password-requirements .wrn-next__strong-password-special {
grid-column: 1 / -1;
white-space: nowrap;
}
.wrn-next__strong-password-special [data-text="specialCharactersSet"] {
white-space: nowrap;
}
.wrn-next__strong-password-requirements li > span[aria-hidden="true"] {
position: relative;
width: 0.9rem;
height: 0.9rem;
@@ -338,51 +341,51 @@ component StrongPassword {
border-radius: 50%;
}
.wire-next__strong-password-requirements li > span::after {
.wrn-next__strong-password-requirements li > span[aria-hidden="true"]::after {
position: absolute;
top: 0.12rem;
left: 0.29rem;
width: 0.22rem;
height: 0.42rem;
border: solid var(--wire-color-surface);
border: solid var(--wrn-color-surface);
border-width: 0 0.1rem 0.1rem 0;
content: "";
opacity: 0;
transform: rotate(45deg);
}
.wire-next__strong-password-requirements li[data-met="true"] {
color: var(--wire-color-success);
.wrn-next__strong-password-requirements li[data-met="true"] {
color: var(--wrn-color-success);
}
.wire-next__strong-password-requirements li[data-met="true"] > span {
border-color: var(--wire-color-success);
background: var(--wire-color-success);
.wrn-next__strong-password-requirements li[data-met="true"] > span[aria-hidden="true"] {
border-color: var(--wrn-color-success);
background: var(--wrn-color-success);
}
.wire-next__strong-password-requirements li[data-met="true"] > span::after {
.wrn-next__strong-password-requirements li[data-met="true"] > span[aria-hidden="true"]::after {
opacity: 1;
}
.wire-next__strong-password-details > small {
color: var(--wire-color-muted);
.wrn-next__strong-password-details > small {
color: var(--wrn-color-muted);
font-size: 0.7em;
line-height: 1.5;
}
.wire-next__strong-password-popover {
.wrn-next__strong-password-popover {
position: absolute;
top: calc(100% + 0.7rem);
left: 0;
z-index: 30;
width: min(28rem, calc(100vw - 2rem));
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
opacity: 0;
visibility: hidden;
background: var(--wire-color-surface);
box-shadow: var(--wire-shadow-2);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-2);
transform: translateY(-0.35rem);
transition:
opacity 140ms ease,
@@ -390,28 +393,587 @@ component StrongPassword {
visibility 140ms ease;
}
.wire-next__strong-password-popover[data-open="true"] {
.wrn-next__strong-password-popover[data-open="true"] {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.wire-next__strong-password-popover-arrow {
.wrn-next__strong-password-popover-arrow {
position: absolute;
top: -0.38rem;
left: 1.25rem;
width: 0.7rem;
height: 0.7rem;
border-top: 1px solid var(--wire-color-border);
border-left: 1px solid var(--wire-color-border);
background: var(--wire-color-surface);
border-top: 1px solid var(--wrn-color-border);
border-left: 1px solid var(--wrn-color-border);
background: var(--wrn-color-surface);
transform: rotate(45deg);
}
@media (max-width: 560px) {
.wire-next__strong-password-requirements {
.wrn-next__strong-password-requirements {
grid-template-columns: 1fr;
}
}
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
/* Shared field foundation. */
/* Validation states set by the browser validation runtime. */
.wrn-invalid {
border-color: var(--wrn-color-danger) !important;
}
.wrn-field-error {
display: block;
min-height: 1em;
margin-top: 0.25rem;
color: var(--wrn-color-danger);
font-size: 0.8rem;
}
.wrn-next--choice {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
}
.wrn-next--choice input {
width: 1.1rem;
height: 1.1rem;
accent-color: var(--wrn-color-primary);
}
.wrn-next--field {
display: grid;
width: min(100%, 28rem);
gap: 0.4rem;
font-size: 0.78rem;
font-weight: 700;
}
.wrn-next--field :where(input, textarea, select) {
width: 100%;
min-height: 2.65rem;
padding: 0.6rem 0.7rem;
border: 1px solid var(--wrn-color-border);
border-radius: 0.6rem;
outline: 0;
color: var(--wrn-color-text);
background: var(--wrn-color-bg);
font: inherit;
font-weight: 500;
}
.wrn-auth-form form > .wrn-next--field,
.wrn-auth-form form > .wrn-field {
width: 100%;
max-width: none;
}
.wrn-auth-form__submit > .wrn-btn {
width: 100%;
min-height: 3rem;
border-radius: 0.75rem;
box-shadow: 0 0.8rem 1.8rem color-mix(in srgb, var(--wrn-btn-color) 20%, transparent);
}
.wrn-next--field :where(input, textarea, select):focus-visible {
border-color: var(--wrn-color-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-primary) 16%, transparent);
}
.wrn-next--field textarea {
resize: vertical;
}
.wrn-next--field,
.wrn-next--choice-field {
--wrn-field-color: var(--wrn-component-color, var(--wrn-color-primary));
width: min(100%, 34rem);
min-width: 0;
color: var(--wrn-color-text);
font-size: 0.8125rem;
font-weight: 500;
}
.wrn-next--field.wrn-next--size-sm,
.wrn-next--choice-field.wrn-next--size-sm {
font-size: 0.8125rem;
}
.wrn-next--field.wrn-next--size-lg,
.wrn-next--choice-field.wrn-next--size-lg {
font-size: 1rem;
}
.wrn-next__field-heading {
display: flex;
min-width: 0;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
}
.wrn-next__field-heading label {
color: var(--wrn-color-text);
font-weight: 600;
}
.wrn-next__field-hint,
.wrn-next__field-help {
color: var(--wrn-color-muted);
font-size: 0.82em;
font-weight: 500;
}
.wrn-next__field-control {
position: relative;
min-width: 0;
}
.wrn-next__field-control > :is(input, textarea, select) {
padding-inline: 0.85rem;
border-color: var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
font-size: 0.8125rem;
}
.wrn-next__field-control[data-icon-position="start"] > :is(input, textarea, select) {
padding-inline-start: 2.5rem;
}
.wrn-next__field-control[data-icon-position="end"] > :is(input, textarea, select) {
padding-inline-end: 2.5rem;
}
.wrn-next__field-icon {
position: absolute;
z-index: 1;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
}
.wrn-next__field-control[data-icon-position="end"] > .wrn-next__field-icon {
right: 0.85rem;
left: auto;
}
.wrn-next--field[data-variant="outlined"]
.wrn-next__field-control
> :is(input, textarea, select) {
border-width: 2px;
}
.wrn-next--field[data-variant="underline"]
.wrn-next__field-control
> :is(input, textarea, select) {
padding-inline: 0;
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.wrn-next--field[data-variant="pill"] .wrn-next__field-control > :is(input, select) {
border-radius: 999px;
padding-inline: 1.15rem;
}
.wrn-next--field[data-variant="ghost"] .wrn-next__field-control > :is(input, textarea, select) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
.wrn-next__field-floating-label {
position: absolute;
top: 50%;
left: 0.85rem;
color: var(--wrn-color-muted);
pointer-events: none;
transform: translateY(-50%);
transition: 150ms ease;
}
.wrn-next--textarea .wrn-next__field-floating-label {
top: 1.35rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-control > :is(input, textarea, select) {
padding-top: 1.2rem;
padding-bottom: 0.3rem;
}
.wrn-next--field[data-floating="true"] .wrn-next__field-heading > label {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-heading:not(:has(.wrn-next__field-hint)) {
display: none;
}
.wrn-next--field[data-floating="true"]
.wrn-next__field-control
> :is(input, textarea):is(:focus, :not(:placeholder-shown))
+ .wrn-next__field-floating-label {
top: 0.42rem;
font-size: 0.7em;
transform: none;
}
.wrn-next__field-error {
min-height: 1em;
color: var(--wrn-color-danger);
font-size: 0.82em;
font-weight: 600;
}
.wrn-next__field-error:empty {
display: none;
}
.wrn-next--field[data-invalid="true"] :is(input, textarea, select),
.wrn-next--choice-field[data-invalid="true"] input {
border-color: var(--wrn-color-danger);
}
.wrn-next--field :is(input, textarea, select):disabled,
.wrn-next--choice-field input:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.wrn-next--field :is(input, textarea, select):read-only {
background: var(--wrn-color-surface-2);
}
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
display: grid;
width: 100%;
max-width: none;
grid-template-columns: minmax(7rem, max-content) minmax(12rem, 20rem) minmax(10rem, 1fr);
justify-content: center;
align-items: center;
gap: 0.75rem;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading {
min-height: 2.65rem;
align-items: center;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-heading {
justify-content: flex-end;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-help,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-help {
display: flex;
min-height: 2.65rem;
align-items: center;
}
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: minmax(12rem, 20rem) minmax(10rem, 1fr);
}
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-heading:empty {
display: none;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: 2 / -1;
}
.wrn-next__sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
border: 0 !important;
margin: -1px !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
}
.wrn-next__choice-field {
display: grid;
width: 100%;
max-width: none;
gap: 0.4rem;
}
.wrn-next__choice {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
color: var(--wrn-color-text);
width: 100%;
font-size: 0.75rem;
font-weight: 500;
}
.wrn-next__checkbox-content > .wrn-next__choice {
width: 100%;
min-width: 0;
align-items: flex-start;
}
.wrn-next__choice input {
flex: 0 0 auto;
margin: 0;
accent-color: var(--wrn-field-color);
}
.wrn-next__choice-copy {
display: grid;
min-width: 0;
gap: 0.15rem;
line-height: 1.35;
}
.wrn-next__choice-copy small {
color: var(--wrn-color-muted);
font-weight: 500;
}
.wrn-next__radio-group,
.wrn-next__checkbox-group {
display: grid;
min-width: 0;
gap: 0.65rem;
}
.wrn-next--radio-field[data-orientation="horizontal"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-orientation="horizontal"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(8rem, max-content));
align-items: stretch;
}
.wrn-next--radio-field[data-card="true"] .wrn-next__radio-group,
.wrn-next--checkbox-field[data-card="true"] .wrn-next__checkbox-group {
grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox {
min-height: 4.5rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
}
.wrn-next--radio-field[data-card="true"] .wrn-next--radio:has(input:checked),
.wrn-next--checkbox-field[data-card="true"] .wrn-next--checkbox:has(input:checked) {
border-color: var(--wrn-field-color);
box-shadow: 0 0 0 1px var(--wrn-field-color);
}
.wrn-next--radio-field[data-list="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-list="true"] .wrn-next--checkbox {
padding-block: 0.75rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox {
justify-content: space-between;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next--radio input,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next--checkbox input {
order: 2;
}
.wrn-next--radio-field[data-right-aligned="true"] .wrn-next__choice-copy,
.wrn-next--checkbox-field[data-right-aligned="true"] .wrn-next__choice-copy {
order: 1;
}
.wrn-next__choice[data-variant="outlined"],
.wrn-next__choice[data-variant="floating"] {
padding: 0.65rem 0.8rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
}
.wrn-next__choice[data-variant="pill"] {
padding: 0.65rem 1rem;
border-radius: 999px;
background: color-mix(in srgb, var(--wrn-field-color) 9%, var(--wrn-color-surface));
}
.wrn-next__choice[data-variant="ghost"] {
padding: 0.65rem 0.8rem;
border-radius: var(--wrn-radius-sm);
background: color-mix(in srgb, var(--wrn-field-color) 7%, transparent);
}
.wrn-next__choice[data-variant="underline"] {
padding-block: 0.5rem;
border-bottom: 1px solid var(--wrn-color-border);
}
.wrn-next--select .wrn-next__field-control {
display: flex;
align-items: center;
}
.wrn-next__color-control,
.wrn-next__range-control,
.wrn-next__input-group-control,
.wrn-next__file-control {
display: flex;
min-width: 0;
align-items: center;
gap: 0.65rem;
}
.wrn-next__picker-control,
.wrn-next--time-picker .wrn-next__field-control {
position: relative;
display: flex;
min-width: 0;
align-items: center;
}
.wrn-next__picker-control > button:first-of-type,
.wrn-next__time-trigger {
display: flex;
width: 100%;
min-height: 2.65rem;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
padding: 0.7rem 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-sm);
background: var(--wrn-color-surface);
color: var(--wrn-color-text);
font: inherit;
text-align: left;
}
.wrn-next__calendar,
.wrn-next__time-panel {
z-index: 20;
display: grid;
width: min(100%, 22rem);
gap: 0.75rem;
padding: 0.85rem;
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius-md);
background: var(--wrn-color-surface);
box-shadow: var(--wrn-shadow-3);
}
.wrn-next--date-picker[data-expanded="false"] .wrn-next__calendar,
.wrn-next--time-picker[data-expanded="false"] .wrn-next__time-panel {
display: none;
}
.wrn-next__calendar button,
.wrn-next__time-panel button {
min-height: 2.25rem;
border: 0;
border-radius: var(--wrn-radius-sm);
background: transparent;
color: inherit;
}
.wrn-next__calendar-weekdays,
.wrn-next__calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.2rem;
text-align: center;
}
.wrn-next__calendar-weekdays {
color: var(--wrn-color-muted);
font-size: 0.75rem;
}
.wrn-next__calendar-grid button:hover,
.wrn-next__time-panel button:hover {
background: var(--wrn-color-surface-2);
}
.wrn-next__calendar-grid button[aria-pressed="true"],
.wrn-next__time-panel button[aria-pressed="true"] {
background: var(--wrn-field-color);
color: var(--wrn-color-primary-contrast, #fff);
}
.wrn-next__time-period {
display: flex;
gap: 0.35rem;
}
.wrn-next--file-upload-progress .wrn-next__row,
.wrn-next__upload-actions {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
}
.wrn-next--file-upload-progress .wrn-next__row > span {
display: grid;
min-width: 0;
}
.wrn-next--field[data-variant="underline"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-width: 0 0 1px;
border-radius: 0;
background: transparent;
}
.wrn-next--field[data-variant="outlined"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-width: 2px;
}
.wrn-next--field[data-variant="pill"]
:is(.wrn-next__input-group-control, .wrn-next__file-control) {
border-radius: 999px;
}
.wrn-next--field[data-variant="ghost"]
:is(.wrn-next__input-group-control, .wrn-next__file-control, .wrn-next__color-control) {
border-color: transparent;
background: color-mix(in srgb, var(--wrn-field-color) 8%, var(--wrn-color-surface));
}
@media (max-width: 640px) {
.wrn-next--field[data-inline="true"],
.wrn-next--choice-field[data-inline="true"] {
grid-template-columns: 1fr;
}
.wrn-next--field[data-inline="true"] .wrn-next__field-error,
.wrn-next--choice-field[data-inline="true"] .wrn-next__field-error {
grid-column: auto;
}
}
.wrn-next--toggle-password:has(input.wrn-invalid) .wrn-next__password-control > input,
.wrn-next--toggle-password.wrn-next--invalid .wrn-next__password-control > input {
border-color: var(--wrn-color-danger);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--wrn-color-danger) 14%, transparent);
}
.wrn-next--strong-password.wrn-next--invalid input,
.wrn-next--strong-password input.wrn-invalid {
border-color: var(--wrn-color-danger);
}
}
}
+79 -13
View File
@@ -1,5 +1,3 @@
import ComponentBaseStyles from "../styles/ComponentBaseStyles.wrn"
component StyledIcon {
props {
size: string = "default"
@@ -11,27 +9,95 @@ component StyledIcon {
class: string = ""
}
view {
<section class="wire-component wire-component--color-{color} wire-component--size-{size} wire-next--styled-icon wire-next--variant-{variant} {class}">
<section class="wrn-component wrn-component--color-{color} wrn-component--size-{size} wrn-next--styled-icon wrn-next--variant-{variant} {class}">
{#if title}<strong>{title}</strong>{/if}
{#if description}<p>{description}</p>{/if}
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
{#if items}<div class="wrn-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
<slot />
</section>
<ComponentBaseStyles hidden />
}
style {
.wire-next--styled-icon {
.wrn-next--styled-icon {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--wire-color-border);
border-radius: var(--wire-radius);
color: var(--wire-color-text);
background: var(--wire-color-surface);
border: 1px solid var(--wrn-color-border);
border-radius: var(--wrn-radius);
color: var(--wrn-color-text);
background: var(--wrn-color-surface);
}
.wire-next--styled-icon > p { margin: 0; color: var(--wire-color-muted); }
.wire-next--styled-icon > .wire-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wire-next--styled-icon > .wire-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wire-radius-sm); background: var(--wire-color-surface-2); }
.wrn-next--styled-icon > p { margin: 0; color: var(--wrn-color-muted); }
.wrn-next--styled-icon > .wrn-next__items { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.wrn-next--styled-icon > .wrn-next__items > span { padding: 0.35rem 0.6rem; border-radius: var(--wrn-radius-sm); background: var(--wrn-color-surface-2); }
/* Shared component foundation. */
.wrn-component {
--wrn-component-color: var(--wrn-color-primary);
--wrn-component-scale: 1;
margin: 0;
color: var(--wrn-color-text);
font-family: var(--wrn-font-sans, inherit);
}
.wrn-component--color-primary {
--wrn-component-color: var(--wrn-color-primary);
}
.wrn-component--color-secondary {
--wrn-component-color: var(--wrn-color-secondary);
}
.wrn-component--color-success {
--wrn-component-color: var(--wrn-color-success);
}
.wrn-component--color-warning {
--wrn-component-color: var(--wrn-color-warning);
}
.wrn-component--color-danger {
--wrn-component-color: var(--wrn-color-danger);
}
.wrn-component--color-info {
--wrn-component-color: var(--wrn-color-info);
}
.wrn-component--size-xs {
--wrn-component-scale: 0.78;
}
.wrn-component--size-sm {
--wrn-component-scale: 0.88;
}
.wrn-component--size-default,
.wrn-component--size-md {
--wrn-component-scale: 1;
}
.wrn-component--size-lg {
--wrn-component-scale: 1.14;
}
.wrn-component--size-xl {
--wrn-component-scale: 1.28;
}
.wrn-component:not(.wrn-btn) {
font-size: calc(1em * var(--wrn-component-scale));
}
.wrn-component :is(a, button, input, select, textarea):focus-visible {
outline-color: var(--wrn-component-color);
}
.wrn-component p {
margin: 0;
color: var(--wrn-color-muted);
line-height: 1.6;
}
}
}

Some files were not shown because too many files have changed in this diff Show More