Compare commits

..
74 Commits
Author SHA1 Message Date
Clintchiz 8b728a3e5d New Captcha Package added 2026-07-25 13:38:18 +05:30
Clintchiz d0aded0392 release: WRNexusJS 0.3.6 2026-07-24 15:30:48 +05:30
Clintchiz c81dedff17 release: WRNexusJS 0.3.5 2026-07-24 12:46:44 +05:30
Clintchiz 44ba847210 release: WRNexusJS 0.3.4 2026-07-22 21:48:39 +05:30
Clintchiz 798f06d4c0 release: WRNexusJS 0.3.4 2026-07-22 21:44:57 +05:30
Clintchiz 173e230d7f release: WRNexusJS 0.3.4 2026-07-22 21:40:38 +05:30
Clintchiz bbec4cc7bb docs(ui): refresh component reference 2026-07-22 21:38:20 +05:30
Clintchiz 504bd4358f release: WRNexusJS 0.3.4 2026-07-22 21:37:23 +05:30
Clintchiz e543fd026d release: WRNexusJS 0.3.4 2026-07-22 21:30:37 +05:30
Clintchiz d51662826d release: WRNexusJS 0.3.4 2026-07-22 21:25:53 +05:30
Clintchiz e2d9ea1ece release: WRNexusJS 0.3.3 2026-07-22 20:37:59 +05:30
Clintchiz 58ba2f2046 release: WRNexusJS 0.3.2 2026-07-22 20:10:44 +05:30
Clintchiz 304819dfe9 release: WRNexusJS 0.3.1 2026-07-22 18:42:20 +05:30
Clintchiz 07d8fb59d6 release: WRNexusJS 0.3.0 2026-07-22 17:29:08 +05:30
Clintchiz 13dfa31d19 release: WRNexusJS 0.2.79 2026-07-22 12:56:07 +05:30
Clintchiz c6fc0f1f63 release: WRNexusJS 0.2.78 2026-07-22 01:53:18 +05:30
Clintchiz 7ff5b3e8c5 release: WRNexusJS 0.2.77 2026-07-22 01:26:10 +05:30
Clintchiz 569365143b release: WRNexusJS 0.2.76 2026-07-21 13:09:09 +05:30
Clintchiz 69b6cd431f release: WRNexusJS 0.2.75 2026-07-21 12:30:46 +05:30
Clintchiz 2ca2d02b22 feat: support WRN imports and dynamic public shell 2026-07-21 11:15:06 +05:30
Clintchiz 0013c0771d chore(release): prepare WRNexusJS 0.2.73 2026-07-20 16:34:20 +05:30
Clintchiz 0b8856b3b5 chore(release): prepare WRNexusJS 0.2.72 2026-07-20 16:15:03 +05:30
Clintchiz 45e6fd3cb9 fix(dev-server): invalidate WRN cache by content 2026-07-20 16:07:38 +05:30
Clintchiz 54f5309fca fix(ui): render polished SSR-safe component variants 2026-07-20 15:55:38 +05:30
Clintchiz 944f83d3f4 fix: use public origins for SSO redirects 2026-07-20 15:04:20 +05:30
Clintchiz a75779fa4d fix: pin production runtime in applications 2026-07-20 14:45:56 +05:30
Clintchiz 2b4083c6db fix: surface app errors in gateway logs 2026-07-20 14:26:27 +05:30
Clintchiz aa2595ec01 fix: log production request failures 2026-07-20 13:48:47 +05:30
Clintchiz b3e5e5b999 feat: configure workspace environment runtimes 2026-07-20 09:22:26 +05:30
Clintchiz 3888453e0d build: stage private packages for 0.2.66 2026-07-20 00:10:16 +05:30
Clintchiz b95bdc6e77 release: prepare WRNexusJS 0.2.66 2026-07-20 00:10:04 +05:30
Clintchiz 1745d8d676 feat: add reusable public chrome and named environments 2026-07-20 00:03:35 +05:30
Clintchiz f72aec7c8c feat(ui): add professional visual fallbacks 2026-07-19 23:09:56 +05:30
Clintchiz 33730c68ab fix(gateway): keep production app ports private 2026-07-19 22:24:28 +05:30
Clintchiz 5ce45b4973 feat(cli): add production workspace command 2026-07-19 21:57:55 +05:30
Clintchiz dcdb5e766d fix(ui): polish linked catalog cards 2026-07-19 21:29:25 +05:30
Clintchiz a3e21163c1 feat(ui): expose app theme contract and public components 2026-07-19 20:23:20 +05:30
Clintchiz c5326f7622 fix: normalize component boundary spacing 2026-07-19 20:08:14 +05:30
Clintchiz eaea2c5f72 fix: keep page shells transparent 2026-07-19 19:52:14 +05:30
Clintchiz ca8f746f41 chore: ignore packaged VS Code extensions 2026-07-19 18:58:31 +05:30
Clintchiz 94ae40d8bd feat: add typed WRN declarations 2026-07-19 18:44:27 +05:30
Clintchiz 0d3ec79ee4 release: complete UI component catalog 0.2.58 2026-07-19 18:14:06 +05:30
Clintchiz c0c09426ff release: WRNexusJS 0.2.57 2026-07-19 17:45:13 +05:30
Clintchiz 14e2878417 fix: tolerate autocrlf publish staging 2026-07-19 17:29:03 +05:30
Clintchiz 180239ef4c release: WRNexusJS 0.2.56 2026-07-19 17:25:37 +05:30
Clintchiz 12beb6f4ba release: WRNexusJS 0.2.55 2026-07-19 17:14:12 +05:30
Clintchiz 6dbc75c370 release: WRNexusJS 0.2.54 2026-07-19 15:51:27 +05:30
Clintchiz 7cee13526e release: WRNexusJS 0.2.53 2026-07-19 14:40:06 +05:30
Clintchiz 3bb096057e release: WRNexusJS 0.2.52 2026-07-19 14:23:10 +05:30
Clintchiz 646d16f83d fix(vscode): repair WRN language diagnostics and activation 2026-07-19 14:02:17 +05:30
Clintchiz df4c1d3e7d release: WRNexusJS 0.2.51 2026-07-19 13:38:23 +05:30
Clintchiz 17dc7c1a06 release: WRNexusJS 0.2.50 2026-07-19 13:08:49 +05:30
Clintchiz c72cbbd780 release: WRNexusJS 0.2.49 2026-07-19 12:29:13 +05:30
Clintchiz 1cda2a01b3 release: WRNexusJS 0.2.49 2026-07-19 12:03:49 +05:30
Clintchiz 9ca1cc6b0f release: WRNexusJS 0.2.49 2026-07-18 19:33:57 +05:30
Clintchiz aa6badbd31 release: WRNexusJS 0.2.48 2026-07-18 19:11:00 +05:30
Clintchiz 4a3ee3baa7 release: WRNexusJS 0.2.48 2026-07-18 19:07:23 +05:30
Clintchiz 95ca1c0625 New UI Components 2026-07-18 16:00:13 +05:30
Clintchiz d22824fa62 release: WRNexusJS 0.2.47 2026-07-18 13:12:16 +05:30
Clintchiz 973d580bc5 release: WRNexusJS 0.2.46 2026-07-15 20:09:47 +05:30
Clintchiz 8a8328a308 release: WRNexusJS 0.2.44 2026-07-15 19:41:42 +05:30
Clintchiz 0f915c3c98 release: WRNexusJS 0.2.44 2026-07-15 18:54:29 +05:30
Clintchiz 9aa13fb4db release: WRNexusJS 0.2.43 2026-07-15 18:41:55 +05:30
Clintchiz 9070a1ef9c release: WRNexusJS 0.2.42 2026-07-15 18:15:22 +05:30
Clintchiz 4230de7065 release: WRNexusJS 0.2.41 2026-07-15 14:52:56 +05:30
Clintchiz 0006aef2c4 release: WRNexusJS 0.2.40 2026-07-15 14:02:27 +05:30
Clintchiz 7dfca4a190 release: WRNexusJS 0.2.40 2026-07-15 13:42:18 +05:30
Clintchiz 29bbeefe92 release: WRNexusJS 0.2.39 2026-07-15 13:22:20 +05:30
Clintchiz 98445c2ec7 release: WRNexusJS 0.2.38 2026-07-15 10:26:24 +05:30
Clintchiz 17a535d4dc release: WRNexusJS 0.2.36 2026-07-15 10:04:14 +05:30
Clintchiz afb14c2fbf release: WRNexusJS 0.2.35 2026-07-15 08:57:07 +05:30
Clintchiz 2c6e6f6de3 release: WRNexusJS 0.2.35 2026-07-15 08:19:54 +05:30
Clintchiz cade812569 release: WRNexusJS 0.2.34 2026-07-15 07:29:59 +05:30
Clintchiz 936488ac06 release: WRNexusJS 0.2.33 2026-07-14 23:13:46 +05:30
695 changed files with 105661 additions and 5305 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ai",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/authz",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/authz — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+12
View File
@@ -36,6 +36,7 @@ Every command accepts an optional `[app-dir]` (defaults to `.`). Commands that r
| `wrnexus workspace <name>` | Scaffold a monorepo (`apps/*` + shared `packages/*`). |
| `wrnexus workspace add <name>` | Add and register an app in the current workspace. |
| `wrnexus gateway [--port=3000]` | Serve every workspace app behind one port, routed by domain. |
| `wrnexus production [workspace-dir]` | Build, migrate, and serve every workspace app in production. |
| `wrnexus generate <type> <name>` | Scaffold a `page` \| `component` \| `api` \| `schema`. |
| `wrnexus generate routes` | Regenerate the typed routes file (`app/routes.gen.ts`). |
| `wrnexus generate docker` | Scaffold `Dockerfile`, `.dockerignore`, and `docker-compose.yml`. |
@@ -168,6 +169,17 @@ wrnexus workspace acme
wrnexus gateway --port=3000
```
For a complete production start, use the first-class workspace orchestrator:
```bash
wrnexus production --host=0.0.0.0 --port=3000
```
It builds every registered app, applies default and named-database SQL migrations
when present, and starts the production gateway only after preparation succeeds.
Use `--prepare-only`, `--no-build`, or `--no-migrate` when deployment stages are
managed separately; `--environment=<name>` selects another workspace environment.
From a workspace root, add and register another app in one command:
```bash
+14 -12
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/cli — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -28,16 +28,18 @@
"wrnexus": "./dist/index.js"
},
"dependencies": {
"@wrnexus/core": "^0.2.32",
"@wrnexus/router": "^0.2.32",
"@wrnexus/csr": "^0.2.32",
"@wrnexus/compiler": "^0.2.32",
"@wrnexus/styles": "^0.2.32",
"@wrnexus/dev-server": "^0.2.32",
"@wrnexus/ui": "^0.2.32",
"@wrnexus/validation": "^0.2.32",
"@wrnexus/i18n": "^0.2.32",
"@wrnexus/db": "^0.2.32"
"@wrnexus/core": "^0.3.6",
"@wrnexus/router": "^0.3.6",
"@wrnexus/csr": "^0.3.6",
"@wrnexus/compiler": "^0.3.6",
"@wrnexus/styles": "^0.3.6",
"@wrnexus/dev-server": "^0.3.6",
"@wrnexus/ui": "^0.3.6",
"@wrnexus/validation": "^0.3.6",
"@wrnexus/i18n": "^0.3.6",
"@wrnexus/db": "^0.3.6",
"@wrnexus/plugin": "^0.3.6",
"@wrnexus/syntax": "^0.3.6"
},
"files": [
"dist"
+31 -15
View File
@@ -8,6 +8,19 @@ Part of the **WrNexus** framework — an SSR-first, Bun-native full-stack web fr
`@wrnexus/compiler` turns `.wrn` source into TypeScript that targets the framework's runtime primitives. A `.wrn` file declares either a `page` (a route) or a `component` (a reusable, prop-driven fragment) with blocks for `state`, `view` (plain HTML), `seo`, `style`, `functions`, `api`, `ssr`/`client` data bindings, and `realtime` websocket handlers. The pipeline is `source → Lexer → parse() → PageAst → generate() → TypeScript`. It is a build/server-side library — the WrNexus dev loader calls it to compile `.wrn` files on the fly, surfacing `ParseError` as a readable error page.
Static ES module imports may appear before the root declaration. Imported values are
available to server-rendered expressions, including component props:
```wrn
import { appUrl } from "@wrnexus/helpers";
layout PublicLayout {
view {
<PublicHeader signInHref="{appUrl('sso', '/sign-in')}" />
}
}
```
## Installation
```bash
@@ -76,18 +89,19 @@ class Lexer {
Exported type-only symbols describing the parsed tree:
| Type | Description |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PageAst` | Root node: `kind` (`"page" \| "component"`), `name`, optional `layout`, `props`, `states`, `seo`, `view`, `styles`, `functions`, `dataApis`, `modeFunctions`, `apis`, `realtimes`. |
| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. |
| `Attr` | `{ name; value; event; boolean? }``event` marks `@event` bindings. |
| `StateDecl` | `{ name; expr }` — a `state x = <expr>` declaration. |
| `SeoBlock` | `Record<string, string>` from the `seo { ... }` block. |
| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. |
| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. |
| `DataMode` | `"ssr" \| "client"`. |
| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. |
| `RealtimeBlock` | `{ name; handlers }` — a `realtime <name> { on evt(args) { ... } }` block. |
| Type | Description |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PageAst` | Root node including top-level `imports`, `kind`, `name`, `types`, typed `props`, typed `states`, `view`, styles, functions, data APIs, lifecycle, and routes. |
| `ViewNode` | `{ type: "text"; value }` or `{ type: "element"; tag; attrs; children }`. |
| `Attr` | `{ name; value; event; boolean? }``event` marks `@event` bindings. |
| `StateDecl` | `{ name; valueType?; expr }` — a typed `state x: Type = <expr>` declaration. |
| `PropDecl` | `{ name; valueType?; required; default }` — a typed prop declaration. |
| `SeoBlock` | `Record<string, string>` from the `seo { ... }` block. |
| `ApiBlock` | `{ method; path; body }` — a top-level `api METHOD /path { ... }`. |
| `DataApiBlock` | `{ mode; name; method; path; body }` — an `api` inside an `ssr`/`client` block. |
| `DataMode` | `"ssr" \| "client"`. |
| `ModeFunctionsBlock` | `{ mode; body }` — a `functions { ... }` inside an `ssr`/`client` block. |
| `RealtimeBlock` | `{ name; handlers }` — a `realtime <name> { on evt(args) { ... } }` block. |
## Usage
@@ -147,12 +161,14 @@ lx.next(); // { type: "lbrace", value: "{", pos: 10 }
A file opens with `page <Name>` or `component <Name>` followed by a `{ ... }` body containing zero or more members:
- `layout = "<name>"` — selects `app/layouts/<name>.wrn` (pages only).
- `props { name = <default> ... }` — component props; each default's type drives coercion.
- `state <ident> = <expr>` — reactive state seeded from a raw JS expression.
- `types { <TypeScript declarations> }` — reusable interfaces and aliases for the current file.
- `props { name: Type = <default> ... }` — typed component props. Omit `= <default>` to make a prop required. Legacy inferred props remain supported.
- `@event name = function` inside `props` — declares a public component event. Emit it from component behavior with `name(detail)` or `$emit("name", detail)`, and consume it with `<Component @name="handler(event)" />`.
- `state <ident>: Type = <expr>` — typed reactive state seeded from a raw JS expression. The annotation is optional for backward compatibility.
- `view { <html> }` — plain HTML with `{expr}` interpolation in text and attributes, hyphenated attributes, boolean attributes, `@event="..."` client bindings, and `<!-- comments -->`. Attribute expressions that reference `state` keep an SSR value and update reactively in the browser.
- `seo { key = "value" ... }` — metadata merged into the generated `meta`.
- `style { <raw css> }` — inlined page/component stylesheet (repeatable).
- `functions { <raw js> }` — shared server-side helpers (repeatable).
- `functions { <TypeScript> }` — helpers with typed parameters and return values. Types remain in server output and are safely erased from browser behavior code.
- `api <METHOD> <path> { <raw js> }` — route handler, lowered to a `METHOD` export (repeatable).
- `ssr { ... }` / `client { ... }` — data blocks holding `api <name> <METHOD> <path> { ... }` bindings and their own `functions { ... }`.
- `realtime <name> { on <evt>(<args>) { <raw js> } ... }` — websocket handlers, lowered to a `websocket` export.
+5 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/compiler",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/compiler — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -20,6 +20,9 @@
"import": "./dist/index.js"
}
},
"dependencies": {
"@wrnexus/syntax": "^0.3.6"
},
"files": [
"dist"
]
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/core",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/core — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+10 -9
View File
@@ -49,15 +49,16 @@ getRealtimeRuntime(): string // → REALTIME_RUNTIME
Applied to any subtree containing `data-scope`. Expressions are parsed by a tiny eval-free evaluator, so a strict CSP with no `unsafe-eval` works.
| Directive | Purpose |
| -------------------------------------------------- | ----------------------------------------------------------------------- |
| `data-scope="count: 0, name: 'x'"` | Declare reactive state on a subtree |
| `data-on-<event>="count++"` | Run a statement in scope on a DOM event |
| `data-text="expr"` | Bind an element's `textContent` to an expression |
| `data-show="expr"` | Toggle visibility (`display`) on truthiness |
| `data-for="item in list"` (opt. `item, i in list`) | Per-item list rendering template |
| `{{expr}}` or `{expr}` | Interpolation inside text nodes and attribute values |
| `data-wrnexus-csr="id"` | Target for a generated CSR fetch binding (fetches `/__wrnexus/csr?...`) |
| Directive | Purpose |
| -------------------------------------------------------- | ----------------------------------------------------------------------- |
| `data-scope="count: 0, name: 'x'"` | Declare reactive state on a subtree |
| `data-on-<event>="count++"` | Run a statement in scope on a DOM event |
| `data-text="expr"` | Bind an element's `textContent` to an expression |
| `data-show="expr"` | Toggle visibility (`display`) on truthiness |
| `data-for="item in list"` (opt. index and `key item.id`) | Per-item rendering; stable keys preserve DOM identity during reorder |
| `data-key="item.id"` | Alternative key declaration for `data-for` templates |
| `{{expr}}` or `{expr}` | Interpolation inside text nodes and attribute values |
| `data-wrnexus-csr="id"` | Target for a generated CSR fetch binding (fetches `/__wrnexus/csr?...`) |
Supported expression features: literals, identifiers, member access (`a.b`, `a[b]`), function/method calls, arrays, objects, arithmetic, comparison, equality, logical (`&& ||`), unary (`! - +`), and ternary. Statements support `++`/`--`, assignment operators (`= += -= *= /= %=`), and bare expression/method calls. Rendering is dependency-tracked: a signal change only re-runs the renderers that actually read it.
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/csr",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/csr — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.32"
"@wrnexus/core": "^0.3.6"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/db",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/db — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+16 -14
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/dev-server",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -25,18 +25,20 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.32",
"@wrnexus/router": "^0.2.32",
"@wrnexus/ssr": "^0.2.32",
"@wrnexus/csr": "^0.2.32",
"@wrnexus/compiler": "^0.2.32",
"@wrnexus/styles": "^0.2.32",
"@wrnexus/ui": "^0.2.32",
"@wrnexus/validation": "^0.2.32",
"@wrnexus/i18n": "^0.2.32",
"@wrnexus/db": "^0.2.32",
"@wrnexus/pubsub": "^0.2.32",
"@wrnexus/uploader": "^0.2.32"
"@wrnexus/core": "^0.3.6",
"@wrnexus/dev-toolbar": "^0.3.6",
"@wrnexus/router": "^0.3.6",
"@wrnexus/ssr": "^0.3.6",
"@wrnexus/csr": "^0.3.6",
"@wrnexus/compiler": "^0.3.6",
"@wrnexus/styles": "^0.3.6",
"@wrnexus/ui": "^0.3.6",
"@wrnexus/validation": "^0.3.6",
"@wrnexus/i18n": "^0.3.6",
"@wrnexus/db": "^0.3.6",
"@wrnexus/pubsub": "^0.3.6",
"@wrnexus/uploader": "^0.3.6",
"@wrnexus/plugin": "^0.3.6"
},
"files": [
"dist"
+23
View File
@@ -0,0 +1,23 @@
# @wrnexus/dev-toolbar
Development-only page quality toolbar for WRNexusJS.
## Features
- Runtime, resource and unhandled promise error capture
- Accessibility, SEO, image, media, color, HTML, form, link, responsive and security checks
- Performance and network observations
- Element highlighting and issue filtering
- Server-side issue collector
- Development-only asset strings for direct serving by `@wrnexus/dev-server`
- Safe open-in-editor helper
## Dev-server integration
Serve `DEV_TOOLBAR_RUNTIME` at `/__wrnexus/dev-toolbar.js` and `DEV_TOOLBAR_CSS` at `/__wrnexus/dev-toolbar.css`, then inject this before `</body>` in development:
```html
<script type="module" src="/__wrnexus/dev-toolbar.js" data-wrnexus-dev-toolbar></script>
```
The browser runtime exposes `window.__wrnexusDevToolbar`.
+42
View File
@@ -0,0 +1,42 @@
{
"name": "@wrnexus/dev-toolbar",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
"license": "MIT",
"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"
},
"./client": {
"types": "./dist/client/index.d.ts",
"import": "./dist/client/index.js"
},
"./server": {
"types": "./dist/server/index.d.ts",
"import": "./dist/server/index.js"
},
"./types": {
"types": "./dist/types.d.ts",
"import": "./dist/types.js"
},
"./rules": {
"types": "./dist/rules/index.d.ts",
"import": "./dist/rules/index.js"
}
},
"files": [
"dist"
]
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/encryption",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/encryption — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/helpers",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.32"
"@wrnexus/core": "^0.3.6"
},
"files": [
"dist"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/i18n",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/i18n — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.32"
"@wrnexus/core": "^0.3.6"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/jwt",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/jwt — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/mobile",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/mobile — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/native": "^0.2.32"
"@wrnexus/native": "^0.3.6"
},
"files": [
"dist"
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/native",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/native — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/oauth",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/oauth — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+7
View File
@@ -0,0 +1,7 @@
# @wrnexus/plugin
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.
+29
View File
@@ -0,0 +1,29 @@
{
"name": "@wrnexus/plugin",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/plugin — part of the WrNexus framework.",
"license": "MIT",
"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/syntax": "^0.3.6"
},
"files": [
"dist"
]
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/pubsub",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/queue",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/queue — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/reactive",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/reactive — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/router",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/router — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,8 +21,8 @@
}
},
"dependencies": {
"@wrnexus/compiler": "^0.2.32",
"@wrnexus/core": "^0.2.32"
"@wrnexus/compiler": "^0.3.6",
"@wrnexus/core": "^0.3.6"
},
"files": [
"dist"
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/ssr",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/ssr — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,7 @@
}
},
"dependencies": {
"@wrnexus/core": "^0.2.32"
"@wrnexus/core": "^0.3.6"
},
"files": [
"dist"
+26 -1
View File
@@ -106,6 +106,7 @@ Tokens are emitted as `--wire-<key>` custom properties, **except** the reserved
type ThemeTokens = Record<string, string>;
interface ThemeConfig {
palette?: ThemePaletteName | CustomThemePalette;
default?: string; // theme used when no cookie is present
themes?: Record<string, ThemeTokens>; // deep-merged over built-in light/dark
}
@@ -117,7 +118,30 @@ interface ResolvedTheme {
}
```
Built-in token keys (both `light` and `dark`): `color-scheme`, `color-bg`, `color-surface`, `color-surface-2`, `color-text`, `color-muted`, `color-border`, `color-primary`, `color-primary-hover`, `color-primary-contrast`, `color-danger`, `color-success`, `color-warning`, `radius`, `radius-sm`, `font-sans`, `shadow-1`.
Built-in palettes are `blue`, `indigo`, `violet`, `emerald`, `cyan`, `rose`,
`amber`, and `slate`. Each supplies primary, secondary, info, success, warning,
danger/error, hover, and contrast colors to every light/dark theme. Built-in
token keys also include surfaces, text, borders, radii, fonts, and shadows.
A custom palette is intentionally complete, so components never fall back to an
unrelated blue status or action color:
```ts
theme: {
palette: {
primary: "#7c3aed",
primaryHover: "#6d28d9",
primaryContrast: "#ffffff",
secondary: "#db2777",
secondaryHover: "#be185d",
secondaryContrast: "#ffffff",
info: "#2563eb",
success: "#059669",
warning: "#d97706",
danger: "#dc2626",
},
}
```
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.
@@ -135,6 +159,7 @@ export default {
port: 3000,
db: { driver: "sqlite", url: "app.db" },
theme: {
palette: "violet",
default: "dark",
themes: {
light: { "color-primary": "#7c3aed" }, // override one token; rest inherited
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/styles",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/styles — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
@@ -21,7 +21,9 @@
}
},
"dependencies": {
"@wrnexus/uploader": "^0.2.32"
"@wrnexus/uploader": "^0.3.6",
"@wrnexus/core": "^0.3.6",
"@wrnexus/plugin": "^0.3.6"
},
"files": [
"dist"
+7
View File
@@ -0,0 +1,7 @@
# @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.
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@wrnexus/syntax",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/syntax — part of the WrNexus framework.",
"license": "MIT",
"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"
}
},
"files": [
"dist"
]
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/test",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/test — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/tracking",
"version": "0.2.32",
"version": "0.3.6",
"type": "module",
"description": "@wrnexus/tracking — part of the WrNexus framework.",
"license": "MIT",
@@ -8,7 +8,7 @@
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"engines": {
"bun": ">=1.1.0"
"bun": ">=1.3.0"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
+784
View File
@@ -0,0 +1,784 @@
# WRNexus UI component reference
This reference is generated from the 85 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
## Advanced-forms
### AdvancedSelect
Theme-aware, responsive advanced select component.
- Mount: `data-component="AdvancedSelect"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Advanced Select"`, `name: string = ""`, `value: string = ""`, `values: string = []`, `options: string = []`, `groups: string = []`, `placeholder: string = "Select an option"`, `placeholderIcon: string = ""`, `searchPlaceholder: string = "Search options…"`, `multiple: boolean = false`, `searchable: boolean = true`, `defaultOpen: boolean = false`, `clearable: boolean = true`, `allowEmpty: boolean = true`, `tags: boolean = false`, `disabled: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `helpText: string = ""`, `loading: boolean = false`, `loadingLabel: string = "Loading options…"`, `emptyLabel: string = "No options found"`, `selectedOptionsLabel: string = "Selected options"`, `clearLabel: string = "Clear selection"`, `createLabel: string = "Create"`, `loadMoreLabel: string = "Load more"`, `searchMode: string = "contains"`, `searchFields: string = "label,description"`, `minSearchLength: number = 0`, `searchResultLimit: number = 0`, `maxSelections: number = 0`, `showCounter: boolean = false`, `counterTemplate: string = "{selected} selected"`, `optionTemplate: string = "default"`, `selectedTemplate: string = "default"`, `closeOnSelect: boolean = true`, `scrollToSelected: boolean = true`, `fixed: boolean = false`, `placement: string = "bottom"`, `remote: boolean = false`, `remoteUrl: string = ""`, `remoteQueryParam: string = "q"`, `remoteDebounce: number = 250`, `remoteAutoLoad: boolean = true`, `infinite: boolean = false`, `hasMore: boolean = false`, `page: number = 1`, `class: string = ""`
- Slots: None
- Events: `search`, `select`, `change`, `clear`, `open`, `close`, `load`, `error`
### ComboBox
Editable autocomplete combobox with local and remote suggestions.
- Mount: `data-component="ComboBox"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "ComboBox"`, `name: string = ""`, `value: string = ""`, `options: string = []`, `groups: string = []`, `placeholder: string = "Search or select an option"`, `searchPlaceholder: string = "Start typing…"`, `clearable: boolean = true`, `allowCustomValue: boolean = false`, `disabled: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `helpText: string = ""`, `loading: boolean = false`, `loadingLabel: string = "Loading suggestions…"`, `emptyLabel: string = "No matching options"`, `clearLabel: string = "Clear value"`, `toggleLabel: string = "Toggle suggestions"`, `searchMode: string = "contains"`, `searchFields: string = "label,description"`, `minSearchLength: number = 0`, `searchResultLimit: number = 0`, `optionTemplate: string = "default"`, `defaultOpen: boolean = false`, `closeOnSelect: boolean = true`, `fixed: boolean = false`, `placement: string = "bottom"`, `autocomplete: string = "off"`, `remote: boolean = false`, `remoteUrl: string = ""`, `remoteQueryParam: string = "q"`, `remoteDebounce: number = 250`, `remoteAutoLoad: boolean = true`, `infinite: boolean = false`, `hasMore: boolean = false`, `page: number = 1`, `loadMoreLabel: string = "Load more"`, `class: string = ""`
- Slots: None
- Events: `search`, `select`, `change`, `clear`, `open`, `close`, `load`, `error`
### CopyMarkup
Theme-aware, responsive copy markup component.
- Mount: `data-component="CopyMarkup"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Copy Markup"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "text"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### InputNumber
Theme-aware, responsive input number component.
- Mount: `data-component="InputNumber"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Input Number"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "number"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### PinInput
Secure multi-cell PIN and verification-code input with regex and paste support.
- Mount: `data-component="PinInput"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Verification code"`, `name: string = "pin"`, `value: string = ""`, `length: number = 4`, `pattern: string = "[0-9]"`, `type: string = "text"`, `inputMode: string = "numeric"`, `placeholder: string = "○"`, `autocomplete: string = "one-time-code"`, `masked: boolean = false`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `autoFocus: boolean = false`, `autoSubmit: boolean = false`, `allowPaste: boolean = true`, `clearable: boolean = true`, `clearLabel: string = "Clear code"`, `separator: string = ""`, `groupSize: number = 0`, `helpText: string = ""`, `invalid: boolean = false`, `validationMessage: string = ""`, `class: string = ""`
- Slots: None
- Events: `input`, `change`, `complete`, `paste`, `clear`, `error`
### SearchBox
Theme-aware, responsive search box component.
- Mount: `data-component="SearchBox"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Search Box"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "search"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### StrongPassword
Theme-aware, responsive strong password component.
- Mount: `data-component="StrongPassword"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Create a strong password"`, `autocomplete: string = "new-password"`, `minLength: number = 8`, `specialCharactersSet: string = "!@#$%^&*()_+-=[]{}|;:,.<>?"`, `requireLowercase: boolean = true`, `requireUppercase: boolean = true`, `requireNumber: boolean = true`, `requireSpecialCharacter: boolean = true`, `showRequirements: boolean = true`, `presentation: string = "inline"`, `hintText: string = "Use a unique password you do not use elsewhere."`, `emptyLabel: string = "Enter a password"`, `weakLabel: string = "Weak"`, `fairLabel: string = "Fair"`, `goodLabel: string = "Good"`, `strongLabel: string = "Strong"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `validationMessage: string = ""`, `class: string = ""`
- Slots: None
- Events: `input`, `change`, `strength`
### ToggleCount
Theme-aware, responsive toggle count component.
- Mount: `data-component="ToggleCount"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Toggle Count"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "text"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### TogglePassword
Accessible password field with optional show and hide controls.
- Mount: `data-component="TogglePassword"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Password"`, `name: string = "password"`, `value: string = ""`, `placeholder: string = "Enter your password"`, `autocomplete: string = "current-password"`, `minlength: string = ""`, `maxlength: string = ""`, `pattern: string = "(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}"`, `fields: string = []`, `visible: boolean = false`, `toggleable: boolean = true`, `toggleMode: string = "button"`, `checkboxLabel: string = "Show password"`, `showLabel: string = "Show password"`, `hideLabel: string = "Hide password"`, `disabled: boolean = false`, `readonly: boolean = false`, `required: boolean = false`, `invalid: boolean = false`, `helpText: string = ""`, `validationMessage: string = ""`, `class: string = ""`
- Slots: None
- Events: `input`, `change`, `toggle`
## Base
### Accordion
Theme-aware, responsive accordion component.
- Mount: `data-component="Accordion"`
- Props: `size: string = "default"`, `color: string = "primary"`, `items: string = []`, `multiple: boolean = false`, `class: string = ""`
- Slots: `default`
- Events: None
### Alert
Theme-aware, responsive alert component.
- Mount: `data-component="Alert"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Alert"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Avatar
Theme-aware, responsive avatar component.
- Mount: `data-component="Avatar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Avatar"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### AvatarGroup
Theme-aware, responsive avatar group component.
- Mount: `data-component="AvatarGroup"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Avatar Group"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Badge
Theme-aware, responsive badge component.
- Mount: `data-component="Badge"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Badge"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Blockquote
Theme-aware, responsive blockquote component.
- Mount: `data-component="Blockquote"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Blockquote"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Button
Theme-aware, responsive button component.
- Mount: `data-component="Button"`
- Props: `label: string = "Button"`, `loadingLabel: string = "Loading…"`, `description: string = ""`, `as: string = ""`, `href: string = ""`, `target: string = ""`, `rel: string = ""`, `type: string = "button"`, `variant: string = "default"`, `color: string = "primary"`, `size: string = "default"`, `disabled: boolean = false`, `loading: boolean = false`, `pill: boolean = false`, `fullWidth: boolean = false`, `icon: string = ""`, `iconPosition: string = "start"`, `ariaLabel: string = ""`, `ariaPressed: string = ""`, `ariaExpanded: string = ""`, `ariaControls: string = ""`, `title: string = ""`, `autofocus: boolean = false`, `controlClass: string = ""`, `class: string = ""`
- Slots: `default`
- Events: None
### ButtonGroup
Theme-aware, responsive button group component.
- Mount: `data-component="ButtonGroup"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Button Group"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Card
Theme-aware, responsive card component.
- Mount: `data-component="Card"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Card"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Carousel
Theme-aware, responsive carousel component.
- Mount: `data-component="Carousel"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Carousel"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### ChatBubble
Theme-aware, responsive chat bubble component.
- Mount: `data-component="ChatBubble"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Chat Bubble"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Collapse
Theme-aware, responsive collapse component.
- Mount: `data-component="Collapse"`
- Props: `size: string = "default"`, `color: string = "primary"`, `items: string = []`, `multiple: boolean = false`, `class: string = ""`
- Slots: `default`
- Events: None
### DatePicker
Theme-aware, responsive date picker component.
- Mount: `data-component="DatePicker"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Date Picker"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "date"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### DeviceFrame
Theme-aware, responsive device frame component.
- Mount: `data-component="DeviceFrame"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Device Frame"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### FileUploadProgress
Theme-aware, responsive file upload progress component.
- Mount: `data-component="FileUploadProgress"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Progress"`, `value: number = 50`, `max: number = 100`, `showValue: boolean = true`, `class: string = ""`
- Slots: None
- Events: None
### LegendIndicator
Theme-aware, responsive legend indicator component.
- Mount: `data-component="LegendIndicator"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Legend Indicator"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### List
Theme-aware, responsive list component.
- Mount: `data-component="List"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "List"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### ListGroup
Theme-aware, responsive list group component.
- Mount: `data-component="ListGroup"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "List Group"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Marquee
Theme-aware, responsive marquee component.
- Mount: `data-component="Marquee"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Marquee"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Progress
Theme-aware, responsive progress component.
- Mount: `data-component="Progress"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Progress"`, `value: number = 50`, `max: number = 100`, `showValue: boolean = true`, `class: string = ""`
- Slots: None
- Events: None
### Rating
Theme-aware, responsive rating component.
- Mount: `data-component="Rating"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Rating"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Skeleton
Theme-aware, responsive skeleton component.
- Mount: `data-component="Skeleton"`
- Props: `color: string = "primary"`, `label: string = "Loading"`, `size: string = "md"`, `lines: number = 3`, `class: string = ""`
- Slots: None
- Events: None
### Spinner
Theme-aware, responsive spinner component.
- Mount: `data-component="Spinner"`
- Props: `color: string = "primary"`, `label: string = "Loading"`, `size: string = "md"`, `lines: number = 3`, `class: string = ""`
- Slots: None
- Events: None
### StyledIcon
Theme-aware, responsive styled icon component.
- Mount: `data-component="StyledIcon"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Styled Icon"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Timeline
Theme-aware, responsive timeline component.
- Mount: `data-component="Timeline"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Timeline"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Toast
Theme-aware, responsive toast component.
- Mount: `data-component="Toast"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Toast"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### TreeView
Theme-aware, responsive tree view component.
- Mount: `data-component="TreeView"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Tree View"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
## Forms
### Checkbox
Theme-aware, responsive checkbox component.
- Mount: `data-component="Checkbox"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Checkbox"`, `name: string = ""`, `value: string = "on"`, `checked: boolean = false`, `disabled: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### ColorPicker
Theme-aware, responsive color picker component.
- Mount: `data-component="ColorPicker"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Color Picker"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "color"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### FileInput
Theme-aware, responsive file input component.
- Mount: `data-component="FileInput"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "File Input"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "file"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### Input
Theme-aware, responsive input component.
- Mount: `data-component="Input"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Input"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "text"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### InputGroup
Theme-aware, responsive input group component.
- Mount: `data-component="InputGroup"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Input Group"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "text"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### Radio
Theme-aware, responsive radio component.
- Mount: `data-component="Radio"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Radio"`, `name: string = ""`, `value: string = "on"`, `checked: boolean = false`, `disabled: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### RangeSlider
Theme-aware, responsive range slider component.
- Mount: `data-component="RangeSlider"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Range Slider"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "range"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### Select
Theme-aware, responsive select component.
- Mount: `data-component="Select"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Select"`, `name: string = ""`, `value: string = ""`, `options: string = []`, `placeholder: string = "Select an option"`, `multiple: boolean = false`, `disabled: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### Switch
Theme-aware, responsive switch component.
- Mount: `data-component="Switch"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Switch"`, `name: string = ""`, `value: string = "on"`, `checked: boolean = false`, `disabled: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### Textarea
Theme-aware, responsive textarea component.
- Mount: `data-component="Textarea"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Textarea"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `rows: number = 5`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
### TimePicker
Theme-aware, responsive time picker component.
- Mount: `data-component="TimePicker"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Time Picker"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "time"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
- Slots: None
- Events: None
## Integrations
### AdvancedDatePicker
Theme-aware, responsive advanced date picker component.
- Mount: `data-component="AdvancedDatePicker"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Advanced Date Picker"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### AdvancedRangeSlider
Theme-aware, responsive advanced range slider component.
- Mount: `data-component="AdvancedRangeSlider"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Advanced Range Slider"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Chart
Theme-aware, responsive chart component.
- Mount: `data-component="Chart"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Chart"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Clipboard
Theme-aware, responsive clipboard component.
- Mount: `data-component="Clipboard"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Clipboard"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Confetti
Theme-aware, responsive confetti component.
- Mount: `data-component="Confetti"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Confetti"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### DataMap
Theme-aware, responsive data map component.
- Mount: `data-component="DataMap"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Data Map"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### DataTable
Theme-aware, responsive data table component.
- Mount: `data-component="DataTable"`
- Props: `size: string = "default"`, `color: string = "primary"`, `caption: string = "Data Table"`, `columns: string = []`, `rows: string = []`, `striped: boolean = true`, `class: string = ""`
- Slots: `default`
- Events: None
### DragAndDrop
Theme-aware, responsive drag and drop component.
- Mount: `data-component="DragAndDrop"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Drag And Drop"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### FileUpload
Theme-aware, responsive file upload component.
- Mount: `data-component="FileUpload"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "File Upload"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### Map
Theme-aware, responsive map component.
- Mount: `data-component="Map"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Map"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### ToastNotifications
Theme-aware, responsive toast notifications component.
- Mount: `data-component="ToastNotifications"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Toast Notifications"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
### WysiwygEditor
Theme-aware, responsive wysiwyg editor component.
- Mount: `data-component="WysiwygEditor"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Wysiwyg Editor"`, `description: string = ""`, `items: string = []`, `variant: string = "default"`, `class: string = ""`
- Slots: `default`
- Events: None
## Layout
### Columns
Theme-aware, responsive columns component.
- Mount: `data-component="Columns"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
### Container
Theme-aware, responsive container component.
- Mount: `data-component="Container"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
### CustomScrollbar
Theme-aware, responsive custom scrollbar component.
- Mount: `data-component="CustomScrollbar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
### Divider
Theme-aware, responsive divider component.
- Mount: `data-component="Divider"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: None
- Events: None
### Grid
Theme-aware, responsive grid component.
- Mount: `data-component="Grid"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
### Image
Theme-aware, responsive image component.
- Mount: `data-component="Image"`
- Props: `size: string = "default"`, `color: string = "primary"`, `src: string = ""`, `alt: string = ""`, `width: string = ""`, `height: string = ""`, `loading: string = "lazy"`, `rounded: boolean = false`, `class: string = ""`
- Slots: `default`
- Events: None
### Kbd
Theme-aware, responsive kbd component.
- Mount: `data-component="Kbd"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "⌘ K"`, `class: string = ""`
- Slots: `default`
- Events: None
### LayoutSplitter
Theme-aware, responsive layout splitter component.
- Mount: `data-component="LayoutSplitter"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
### Link
Theme-aware, responsive link component.
- Mount: `data-component="Link"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Link"`, `href: string = "#"`, `target: string = ""`, `rel: string = ""`, `external: boolean = false`, `class: string = ""`
- Slots: `default`
- Events: None
### Typography
Theme-aware, responsive typography component.
- Mount: `data-component="Typography"`
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
- Slots: `default`
- Events: None
## Navigation
### Breadcrumb
Theme-aware, responsive breadcrumb component.
- Mount: `data-component="Breadcrumb"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Breadcrumb"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### MegaMenu
Theme-aware, responsive mega menu component.
- Mount: `data-component="MegaMenu"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Mega Menu"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Nav
Theme-aware, responsive nav component.
- Mount: `data-component="Nav"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Nav"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Navbar
Theme-aware, responsive navbar component.
- Mount: `data-component="Navbar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Navbar"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Pagination
Theme-aware, responsive pagination component.
- Mount: `data-component="Pagination"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Pagination"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Scrollspy
Theme-aware, responsive scrollspy component.
- Mount: `data-component="Scrollspy"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Scrollspy"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Sidebar
Theme-aware, responsive sidebar component.
- Mount: `data-component="Sidebar"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Sidebar"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Stepper
Theme-aware, responsive stepper component.
- Mount: `data-component="Stepper"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Stepper"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
### Tabs
Theme-aware, responsive tabs component.
- Mount: `data-component="Tabs"`
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Tabs"`, `items: string = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
- Slots: `default`
- Events: None
## Overlays
### ContextMenu
Theme-aware, responsive context menu component.
- Mount: `data-component="ContextMenu"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Context Menu"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
### Drawer
Theme-aware, responsive drawer component.
- Mount: `data-component="Drawer"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Drawer"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
### Dropdown
Theme-aware, responsive dropdown component.
- Mount: `data-component="Dropdown"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Dropdown"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
### Modal
Theme-aware, responsive modal component.
- Mount: `data-component="Modal"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Modal"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
### Popover
Theme-aware, responsive popover component.
- Mount: `data-component="Popover"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Popover"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
### Tooltip
Theme-aware, responsive tooltip component.
- Mount: `data-component="Tooltip"`
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Tooltip"`, `description: string = ""`, `open: boolean = false`, `placement: string = "bottom"`, `closeLabel: string = "Close"`, `class: string = ""`
- Slots: `default`
- Events: None
## Tables
### Table
Theme-aware, responsive table component.
- Mount: `data-component="Table"`
- Props: `size: string = "default"`, `color: string = "primary"`, `caption: string = "Table"`, `columns: string = []`, `rows: string = []`, `striped: boolean = true`, `class: string = ""`
- Slots: `default`
- Events: None
+67 -1
View File
@@ -15,6 +15,11 @@ driven by `var(--wire-*)` 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.
The complete PDF-aligned catalog currently contains **891 components**. The
generated `COMPONENTS.md` and `component-reference.json` files document every
mount name, prop, inferred type, default/required status, slot, event, category,
and source file directly from the packaged `.wrn` source.
## Installation
```bash
@@ -65,11 +70,25 @@ body from either a named prop or the default slot.
### Additional controls & data display
Also shipped: `select`, `radio`, `switch`, `progress`, `tag`, `skeleton`,
`tooltip`, and `table`.
`tooltip`, `table`, `FAQAccordion`, `AnnouncementBar`, and `BackToTop`.
The PDF-defined minimum release and essential build-first set also includes
typed typography, form primitives, loading actions, combobox and multi-select,
time/date-time and recurring schedule controls, confirmation dialogs, data
tables, filters, desktop/mobile navigation, mega menus, marketing/product/legal
page shells, product and metric cards, FAQ composition, pricing comparison,
SDK tabs, legal navigation, and cookie preferences.
`Seo` and `StructuredData` remain framework/page concerns rather than body
components: use the native page `seo { ... }` block and document-head APIs so
metadata is emitted in `<head>` instead of invalid component markup.
The authoritative, always-current list is `uiComponentNames()` (below), which reads
the component directory at runtime.
For the full catalog, see [`COMPONENTS.md`](./COMPONENTS.md). The machine-readable
equivalent is exported as `@wrnexus/ui/component-reference.json`.
## API
The JS module (`@wrnexus/ui`) exposes four helpers used by the build tooling to
@@ -97,6 +116,53 @@ are `.wrn` files rendered server-side.
The framework serves this stylesheet once at `/__wrnexus/ui.css`, so pages get all
component styles from a single request.
### Tailwind and motion
Components use static Tailwind utility classes alongside the shared `.wire-*`
layer. If the package is consumed by a separate Tailwind build, include its
component sources so every utility is generated:
```css
@import "tailwindcss";
@source "../node_modules/@wrnexus/ui/components/*.wrn";
```
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
devices and `prefers-reduced-motion` is honored automatically.
### Using the selected theme in application UI
The active theme and palette are not limited to `@wrnexus/ui` components. The
framework exposes the resolved values as semantic CSS custom properties, so
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);
}
.account-card__action {
background: var(--wire-color-primary);
color: var(--wire-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`.
Tailwind-authored custom markup can continue using the palette families already
used by packaged components. `indigo-*` and `violet-*` resolve to primary,
`blue-*` to info, `emerald-*`/`green-*` to success, `amber-*` to warning, and
`red-*`/`rose-*` to danger. These aliases live at `:root`, so they work outside
a `[data-component]` boundary too.
## Usage
### Auto-discovery
+434
View File
@@ -0,0 +1,434 @@
{
"sourceDocuments": ["Screenshot-directed WRNexus UI catalog reset"],
"components": [
{
"name": "Container",
"category": "layout",
"purpose": "Theme-aware, responsive container component."
},
{
"name": "Columns",
"category": "layout",
"purpose": "Theme-aware, responsive columns component."
},
{
"name": "Grid",
"category": "layout",
"purpose": "Theme-aware, responsive grid component."
},
{
"name": "LayoutSplitter",
"category": "layout",
"purpose": "Theme-aware, responsive layout splitter component."
},
{
"name": "Typography",
"category": "layout",
"purpose": "Theme-aware, responsive typography component."
},
{
"name": "Image",
"category": "layout",
"purpose": "Theme-aware, responsive image component."
},
{
"name": "Link",
"category": "layout",
"purpose": "Theme-aware, responsive link component."
},
{
"name": "Divider",
"category": "layout",
"purpose": "Theme-aware, responsive divider component."
},
{
"name": "Kbd",
"category": "layout",
"purpose": "Theme-aware, responsive kbd component."
},
{
"name": "CustomScrollbar",
"category": "layout",
"purpose": "Theme-aware, responsive custom scrollbar component."
},
{
"name": "Accordion",
"category": "base",
"purpose": "Theme-aware, responsive accordion component."
},
{
"name": "Alert",
"category": "base",
"purpose": "Theme-aware, responsive alert component."
},
{
"name": "Avatar",
"category": "base",
"purpose": "Theme-aware, responsive avatar component."
},
{
"name": "AvatarGroup",
"category": "base",
"purpose": "Theme-aware, responsive avatar group component."
},
{
"name": "Badge",
"category": "base",
"purpose": "Theme-aware, responsive badge component."
},
{
"name": "Blockquote",
"category": "base",
"purpose": "Theme-aware, responsive blockquote component."
},
{
"name": "Button",
"category": "base",
"purpose": "Theme-aware, responsive button component."
},
{
"name": "ButtonGroup",
"category": "base",
"purpose": "Theme-aware, responsive button group component."
},
{
"name": "Card",
"category": "base",
"purpose": "Theme-aware, responsive card component."
},
{
"name": "ChatBubble",
"category": "base",
"purpose": "Theme-aware, responsive chat bubble component."
},
{
"name": "Carousel",
"category": "base",
"purpose": "Theme-aware, responsive carousel component."
},
{
"name": "Collapse",
"category": "base",
"purpose": "Theme-aware, responsive collapse component."
},
{
"name": "DatePicker",
"category": "base",
"purpose": "Theme-aware, responsive date picker component."
},
{
"name": "DeviceFrame",
"category": "base",
"purpose": "Theme-aware, responsive device frame component."
},
{
"name": "List",
"category": "base",
"purpose": "Theme-aware, responsive list component."
},
{
"name": "ListGroup",
"category": "base",
"purpose": "Theme-aware, responsive list group component."
},
{
"name": "LegendIndicator",
"category": "base",
"purpose": "Theme-aware, responsive legend indicator component."
},
{
"name": "Progress",
"category": "base",
"purpose": "Theme-aware, responsive progress component."
},
{
"name": "FileUploadProgress",
"category": "base",
"purpose": "Theme-aware, responsive file upload progress component."
},
{
"name": "Rating",
"category": "base",
"purpose": "Theme-aware, responsive rating component."
},
{
"name": "Skeleton",
"category": "base",
"purpose": "Theme-aware, responsive skeleton component."
},
{
"name": "Spinner",
"category": "base",
"purpose": "Theme-aware, responsive spinner component."
},
{
"name": "StyledIcon",
"category": "base",
"purpose": "Theme-aware, responsive styled icon component."
},
{
"name": "Toast",
"category": "base",
"purpose": "Theme-aware, responsive toast component."
},
{
"name": "Timeline",
"category": "base",
"purpose": "Theme-aware, responsive timeline component."
},
{
"name": "TreeView",
"category": "base",
"purpose": "Theme-aware, responsive tree view component."
},
{
"name": "Marquee",
"category": "base",
"purpose": "Theme-aware, responsive marquee component."
},
{
"name": "Navbar",
"category": "navigation",
"purpose": "Theme-aware, responsive navbar component."
},
{
"name": "MegaMenu",
"category": "navigation",
"purpose": "Theme-aware, responsive mega menu component."
},
{
"name": "Nav",
"category": "navigation",
"purpose": "Theme-aware, responsive nav component."
},
{
"name": "Tabs",
"category": "navigation",
"purpose": "Theme-aware, responsive tabs component."
},
{
"name": "Sidebar",
"category": "navigation",
"purpose": "Theme-aware, responsive sidebar component."
},
{
"name": "Scrollspy",
"category": "navigation",
"purpose": "Theme-aware, responsive scrollspy component."
},
{
"name": "Breadcrumb",
"category": "navigation",
"purpose": "Theme-aware, responsive breadcrumb component."
},
{
"name": "Pagination",
"category": "navigation",
"purpose": "Theme-aware, responsive pagination component."
},
{
"name": "Stepper",
"category": "navigation",
"purpose": "Theme-aware, responsive stepper component."
},
{
"name": "Input",
"category": "forms",
"purpose": "Theme-aware, responsive input component."
},
{
"name": "InputGroup",
"category": "forms",
"purpose": "Theme-aware, responsive input group component."
},
{
"name": "Textarea",
"category": "forms",
"purpose": "Theme-aware, responsive textarea component."
},
{
"name": "FileInput",
"category": "forms",
"purpose": "Theme-aware, responsive file input component."
},
{
"name": "Checkbox",
"category": "forms",
"purpose": "Theme-aware, responsive checkbox component."
},
{
"name": "Radio",
"category": "forms",
"purpose": "Theme-aware, responsive radio component."
},
{
"name": "Switch",
"category": "forms",
"purpose": "Theme-aware, responsive switch component."
},
{
"name": "Select",
"category": "forms",
"purpose": "Theme-aware, responsive select component."
},
{
"name": "RangeSlider",
"category": "forms",
"purpose": "Theme-aware, responsive range slider component."
},
{
"name": "ColorPicker",
"category": "forms",
"purpose": "Theme-aware, responsive color picker component."
},
{
"name": "TimePicker",
"category": "forms",
"purpose": "Theme-aware, responsive time picker component."
},
{
"name": "AdvancedSelect",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive advanced select component.",
"events": ["search", "select", "change", "clear", "open", "close", "load", "error"]
},
{
"name": "ComboBox",
"category": "advanced-forms",
"purpose": "Editable autocomplete combobox with local and remote suggestions.",
"events": ["search", "select", "change", "clear", "open", "close", "load", "error"]
},
{
"name": "SearchBox",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive search box component."
},
{
"name": "InputNumber",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive input number component."
},
{
"name": "StrongPassword",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive strong password component."
},
{
"name": "TogglePassword",
"category": "advanced-forms",
"purpose": "Accessible password field with optional show and hide controls.",
"events": ["input", "change", "toggle"]
},
{
"name": "ToggleCount",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive toggle count component."
},
{
"name": "CopyMarkup",
"category": "advanced-forms",
"purpose": "Theme-aware, responsive copy markup component."
},
{
"name": "PinInput",
"category": "advanced-forms",
"purpose": "Secure multi-cell PIN and verification-code input with regex and paste support.",
"events": ["input", "change", "complete", "paste", "clear", "error"]
},
{
"name": "Dropdown",
"category": "overlays",
"purpose": "Theme-aware, responsive dropdown component."
},
{
"name": "ContextMenu",
"category": "overlays",
"purpose": "Theme-aware, responsive context menu component."
},
{
"name": "Modal",
"category": "overlays",
"purpose": "Theme-aware, responsive modal component."
},
{
"name": "Drawer",
"category": "overlays",
"purpose": "Theme-aware, responsive drawer component."
},
{
"name": "Popover",
"category": "overlays",
"purpose": "Theme-aware, responsive popover component."
},
{
"name": "Tooltip",
"category": "overlays",
"purpose": "Theme-aware, responsive tooltip component."
},
{
"name": "Table",
"category": "tables",
"purpose": "Theme-aware, responsive table component."
},
{
"name": "AdvancedRangeSlider",
"category": "integrations",
"purpose": "Theme-aware, responsive advanced range slider component."
},
{
"name": "AdvancedDatePicker",
"category": "integrations",
"purpose": "Theme-aware, responsive advanced date picker component."
},
{
"name": "Chart",
"category": "integrations",
"purpose": "Theme-aware, responsive chart component."
},
{
"name": "Clipboard",
"category": "integrations",
"purpose": "Theme-aware, responsive clipboard component."
},
{
"name": "Confetti",
"category": "integrations",
"purpose": "Theme-aware, responsive confetti component."
},
{
"name": "DataMap",
"category": "integrations",
"purpose": "Theme-aware, responsive data map component."
},
{
"name": "DataTable",
"category": "integrations",
"purpose": "Theme-aware, responsive data table component."
},
{
"name": "DragAndDrop",
"category": "integrations",
"purpose": "Theme-aware, responsive drag and drop component."
},
{
"name": "FileUpload",
"category": "integrations",
"purpose": "Theme-aware, responsive file upload component."
},
{
"name": "Map",
"category": "integrations",
"purpose": "Theme-aware, responsive map component."
},
{
"name": "ToastNotifications",
"category": "integrations",
"purpose": "Theme-aware, responsive toast notifications component."
},
{
"name": "WysiwygEditor",
"category": "integrations",
"purpose": "Theme-aware, responsive wysiwyg editor component."
}
]
}
+5
View File
@@ -0,0 +1,5 @@
{
"generatedFrom": "full component catalog reset",
"removedCount": 0,
"replacements": {}
}
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
component Accordion {
props {
size = "default"
color = "primary"
items = []
multiple = false
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--accordion {class}">
{#each items as item}<details open="{item.open}"><summary>{item.label}</summary><div>{item.content}</div></details>{/each}
<slot />
</div>
}
}
@@ -0,0 +1,19 @@
component AdvancedDatePicker {
props {
size = "default"
color = "primary"
title = "Advanced Date Picker"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--advanced-date-picker wire-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}
<slot />
</section>
}
}
@@ -0,0 +1,19 @@
component AdvancedRangeSlider {
props {
size = "default"
color = "primary"
title = "Advanced Range Slider"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--advanced-range-slider wire-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}
<slot />
</section>
}
}
+456
View File
@@ -0,0 +1,456 @@
component AdvancedSelect {
props {
size = "default"
color = "primary"
label = "Advanced Select"
name = ""
value = ""
values = []
options = []
groups = []
placeholder = "Select an option"
placeholderIcon = ""
searchPlaceholder = "Search options…"
multiple = false
searchable = true
defaultOpen = false
clearable = true
allowEmpty = true
tags = false
disabled = false
required = false
invalid = false
validationMessage = ""
helpText = ""
loading = false
loadingLabel = "Loading options…"
emptyLabel = "No options found"
selectedOptionsLabel = "Selected options"
clearLabel = "Clear selection"
createLabel = "Create"
loadMoreLabel = "Load more"
searchMode = "contains"
searchFields = "label,description"
minSearchLength = 0
searchResultLimit = 0
maxSelections = 0
showCounter = false
counterTemplate = "{selected} selected"
optionTemplate = "default"
selectedTemplate = "default"
closeOnSelect = true
scrollToSelected = true
fixed = false
placement = "bottom"
remote = false
remoteUrl = ""
remoteQueryParam = "q"
remoteDebounce = 250
remoteAutoLoad = true
infinite = false
hasMore = false
page = 1
class = ""
@event search = function
@event select = function
@event change = function
@event clear = function
@event open = function
@event close = function
@event load = function
@event error = function
}
state open = defaultOpen
state query = ""
state activeIndex = -1
state selectedValue = value
state selectedValues = values
functions {
function allOptions() {
return [...groups.flatMap((group) => group.options || []), ...options]
}
function searchableText(option) {
return ((option.label || "") + " " + (option.description || "")).toLowerCase()
}
function matches(option) {
if (!query || query.length < Number(minSearchLength || 0)) {
return true
}
if (searchMode === "startsWith") {
return searchableText(option).startsWith(query.toLowerCase())
}
if (searchMode === "exact") {
return searchableText(option) === query.toLowerCase()
}
return searchableText(option).includes(query.toLowerCase())
}
function matchingOptions(list) {
return list.filter((option) => matches(option))
}
function visibleOptions(list) {
if (searchResultLimit > 0) {
return matchingOptions(list).slice(0, Number(searchResultLimit))
}
return matchingOptions(list)
}
function flatVisibleOptions() {
return visibleOptions(allOptions())
}
function isSelected(option) {
return (
multiple
? selectedValues.includes(option.value)
: selectedValue === option.value
)
}
function selectedOptions() {
return allOptions().filter((option) => isSelected(option))
}
function selectedCount() {
return multiple ? selectedValues.length : selectedValue ? 1 : 0
}
function counterText() {
return (
maxSelections
? selectedCount() + " / " + maxSelections + " selected"
: selectedCount() + " selected"
)
}
function selectedText() {
return (
multiple
? selectedValues.join(", ")
: selectedOptions().length
? selectedOptions()[0].label
: ""
)
}
function triggerText() {
return selectedCount() ? selectedText() : placeholder
}
function canSelect(option) {
if (option.disabled) {
return false
}
if (!multiple || isSelected(option)) {
return true
}
if (maxSelections <= 0) {
return true
}
return selectedCount() < maxSelections
}
function chooseSingle(option) {
if (!canSelect(option)) {
return
}
selectedValue = option.value
query = ""
if (closeOnSelect) {
open = false
}
}
function chooseMultiple(option) {
if (!canSelect(option)) {
return
}
if (isSelected(option)) {
selectedValues = selectedValues.filter((item) => item !== option.value)
} else {
selectedValues = selectedValues.concat([option.value])
}
query = ""
}
function chooseOption(option) {
if (multiple) {
chooseMultiple(option)
return
}
chooseSingle(option)
}
function clearSelection(event) {
event.stopPropagation()
selectedValue = ""
selectedValues = []
query = ""
open = false
}
function toggle() {
if (disabled) {
return;
};
open = !open;
activeIndex = open && flatVisibleOptions().length ? 0 : -1;
}
function moveActive(direction) {
if (!flatVisibleOptions().length) {
return
}
activeIndex = (activeIndex + direction + flatVisibleOptions().length) % flatVisibleOptions().length
}
function handleKeydown(event) {
if (disabled) {
return
}
if (event.key === "ArrowDown") {
event.preventDefault()
if (!open) {
open = true
}
moveActive(1)
} else if (event.key === "ArrowUp") {
event.preventDefault()
if (!open) {
open = true
}
moveActive(-1)
} else if (event.key === "Enter" || event.key === " ") {
event.preventDefault()
if (!open) {
open = true
} else if (activeIndex >= 0) {
chooseOption(flatVisibleOptions()[activeIndex])
}
} else if (event.key === "Escape") {
open = false
} else if (event.key === "Home" && open) {
event.preventDefault()
activeIndex = 0
} else if (event.key === "End" && open) {
event.preventDefault()
activeIndex = flatVisibleOptions().length - 1
}
}
function optionIndex(option) {
return flatVisibleOptions().findIndex((item) => item.value === option.value)
}
}
view {
<div
class="wire-next wire-next--color-{color} wire-next--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}"
data-placement="{placement}"
data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}"
data-remote-url="{remoteUrl}"
data-remote-query-param="{remoteQueryParam}"
data-remote-debounce="{remoteDebounce}"
data-remote-auto-load="{remoteAutoLoad ? 'true' : 'false'}"
data-infinite="{infinite ? 'true' : 'false'}"
data-page="{page}"
data-wrn-select
@focusout="if (!event.currentTarget.contains(event.relatedTarget)) { open = false }"
>
<div class="wire-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">
<button
{...attrs}
id="{name}-trigger"
type="button"
class="wire-next__select-trigger"
role="combobox"
aria-haspopup="listbox"
aria-expanded="{open}"
aria-controls="{name}-listbox"
aria-labelledby="{name}-label"
aria-invalid="{invalid}"
disabled="{disabled}"
@click="toggle()"
@keydown="handleKeydown(event)"
>
<span class="wire-next__select-value">
<span data-show="!multiple" data-text="triggerText()">{triggerText()}</span>
<span
data-show="multiple && selectedTemplate === 'count' && selectedCount() > 0"
data-text="selectedCount() + ' selected'"
>{selectedCount()} selected</span>
<span
data-show="multiple && selectedTemplate === 'text' && selectedCount() > 0"
data-text="selectedText()"
>{selectedText()}</span>
<span
class="wire-next__select-tags"
aria-label="{selectedOptionsLabel}"
data-show="multiple && selectedTemplate !== 'count' && selectedTemplate !== 'text' && selectedCount() > 0"
>
{#each allOptions() as option}
<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}
{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>
</span>
<span
class="wire-next__select-chevron icon-[lucide--chevrons-up-down]"
aria-hidden="true"
></span>
</button>
<button
type="button"
class="wire-next__clear-select"
data-show="clearable && allowEmpty && selectedCount() > 0 && !disabled"
aria-label="{clearLabel}"
title="{clearLabel}"
@click="clearSelection(event)"
>
<span class="icon-[lucide--x]" aria-hidden="true"></span>
</button>
</div>
<div
class="wire-next__select-dropdown {fixed ? 'wire-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>
<input
type="search"
value="{query}"
placeholder="{searchPlaceholder}"
autocomplete="off"
@input="query = event.target.value; activeIndex = flatVisibleOptions().length ? 0 : -1"
@keydown="handleKeydown(event)"
/>
</label>
{/if}
<div
class="wire-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>
{loadingLabel}
</div>
<div
id="{name}-listbox"
class="wire-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>
{#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' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
aria-selected="{isSelected(option)}"
disabled="{!canSelect(option)}"
@mouseenter="activeIndex = optionIndex(option)"
@click="chooseOption(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}
<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>
</button>
{/each}
</div>
{/if}
{/each}
{#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' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
aria-selected="{isSelected(option)}"
disabled="{!canSelect(option)}"
@mouseenter="activeIndex = optionIndex(option)"
@click="chooseOption(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}
<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>
</button>
{/each}
{#if !loading && !flatVisibleOptions().length}
<div class="wire-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>
{/if}
<button
type="button"
class="wire-next__load-more"
data-wrn-select-load-more
data-show="infinite && hasMore"
>{loadMoreLabel}</button>
</div>
<input
type="hidden"
name="{name}"
value="{multiple ? selectedValues.join(',') : selectedValue}"
required="{required}"
aria-describedby="{validationMessage ? `${name}-validation` : helpText ? `${name}-help` : ''}"
/>
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
data-error="{name}"
>{validationMessage}</small>
</div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component AvatarGroup {
props {
size = "default"
color = "primary"
title = "Avatar Group"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--avatar-group wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Blockquote {
props {
size = "default"
color = "primary"
title = "Blockquote"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--blockquote wire-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}
<slot />
</section>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Breadcrumb {
props {
size = "default"
color = "primary"
label = "Breadcrumb"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--breadcrumb wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+19
View File
@@ -0,0 +1,19 @@
component ButtonGroup {
props {
size = "default"
color = "primary"
title = "Button Group"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--button-group wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Carousel {
props {
size = "default"
color = "primary"
title = "Carousel"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--carousel wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Chart {
props {
size = "default"
color = "primary"
title = "Chart"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--chart wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component ChatBubble {
props {
size = "default"
color = "primary"
title = "Chat Bubble"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--chat-bubble wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Clipboard {
props {
size = "default"
color = "primary"
title = "Clipboard"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--clipboard wire-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}
<slot />
</section>
}
}
+15
View File
@@ -0,0 +1,15 @@
component Collapse {
props {
size = "default"
color = "primary"
items = []
multiple = false
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--collapse {class}">
{#each items as item}<details open="{item.open}"><summary>{item.label}</summary><div>{item.content}</div></details>{/each}
<slot />
</div>
}
}
+20
View File
@@ -0,0 +1,20 @@
component ColorPicker {
props {
size = "default"
color = "primary"
label = "Color Picker"
name = ""
value = ""
placeholder = ""
type = "color"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--field wire-next--color-picker {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+13
View File
@@ -0,0 +1,13 @@
component Columns {
props {
size = "default"
color = "primary"
columns = 2
gap = "md"
maxWidth = "xl"
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--columns wire-next--gap-{gap} wire-next--columns-{columns} wire-next--max-{maxWidth} {class}"><slot /></div>
}
}
+386
View File
@@ -0,0 +1,386 @@
component ComboBox {
props {
size = "default"
color = "primary"
label = "ComboBox"
name = ""
value = ""
options = []
groups = []
placeholder = "Search or select an option"
searchPlaceholder = "Start typing…"
clearable = true
allowCustomValue = false
disabled = false
required = false
invalid = false
validationMessage = ""
helpText = ""
loading = false
loadingLabel = "Loading suggestions…"
emptyLabel = "No matching options"
clearLabel = "Clear value"
toggleLabel = "Toggle suggestions"
searchMode = "contains"
searchFields = "label,description"
minSearchLength = 0
searchResultLimit = 0
optionTemplate = "default"
defaultOpen = false
closeOnSelect = true
fixed = false
placement = "bottom"
autocomplete = "off"
remote = false
remoteUrl = ""
remoteQueryParam = "q"
remoteDebounce = 250
remoteAutoLoad = true
infinite = false
hasMore = false
page = 1
loadMoreLabel = "Load more"
class = ""
@event search = function
@event select = function
@event change = function
@event clear = function
@event open = function
@event close = function
@event load = function
@event error = function
}
state open = defaultOpen
state query = ""
state selectedValue = value
state activeIndex = -1
functions {
function allOptions() {
return [...groups.flatMap((group) => group.options || []), ...options]
}
function searchableText(option) {
return ((option.label || "") + " " + (option.description || "")).toLowerCase()
}
function matches(option) {
if (!query || query.length < minSearchLength) {
return true
}
if (searchMode === "startsWith") {
return searchableText(option).startsWith(query.toLowerCase())
}
if (searchMode === "exact") {
return searchableText(option) === query.toLowerCase()
}
return searchableText(option).includes(query.toLowerCase())
}
function matchingOptions(list) {
return list.filter((option) => matches(option))
}
function visibleOptions(list) {
if (searchResultLimit > 0) {
return matchingOptions(list).slice(0, searchResultLimit)
}
return matchingOptions(list)
}
function flatVisibleOptions() {
return visibleOptions(allOptions())
}
function isSelected(option) {
return selectedValue === option.value
}
function selectedOptions() {
return allOptions().filter((option) => isSelected(option))
}
function selectedText() {
return selectedOptions().length ? selectedOptions()[0].label : selectedValue
}
function inputText() {
return query || selectedText()
}
function shouldShowDropdown() {
if (!open) {
return false
}
if (loading) {
return true
}
if (remote && query.length !== 0 && query.length < minSearchLength) {
return true
}
if (flatVisibleOptions().length) {
return true
}
return infinite && hasMore
}
function canSelect(option) {
return !option.disabled
}
function chooseOption(option) {
if (!canSelect(option)) {
return
}
selectedValue = option.value
query = option.label
activeIndex = optionIndex(option)
if (closeOnSelect) {
open = false
}
}
function updateQuery(event) {
query = event.target.value
selectedValue = allowCustomValue ? event.target.value : ""
open = true
activeIndex = flatVisibleOptions().length ? 0 : -1
}
function clearValue(event) {
if (event) {
event.stopPropagation()
}
query = ""
selectedValue = ""
activeIndex = -1
open = false
}
function openDropdown() {
if (!disabled) {
open = true
activeIndex = flatVisibleOptions().length ? 0 : -1
}
}
function closeDropdown() {
open = false
}
function toggle() {
if (open) {
closeDropdown()
} else {
openDropdown()
}
}
function setValue(nextValue) {
selectedValue = nextValue
query = ""
}
function moveActive(direction) {
if (!flatVisibleOptions().length) {
return
}
activeIndex = (activeIndex + direction + flatVisibleOptions().length) % flatVisibleOptions().length
}
function handleKeydown(event) {
if (disabled) {
return
}
if (event.key === "ArrowDown") {
event.preventDefault()
openDropdown()
moveActive(1)
} else if (event.key === "ArrowUp") {
event.preventDefault()
openDropdown()
moveActive(-1)
} else if (event.key === "Enter" && open && activeIndex >= 0) {
event.preventDefault()
chooseOption(flatVisibleOptions()[activeIndex])
} else if (event.key === "Escape") {
closeDropdown()
} else if (event.key === "Home" && open) {
event.preventDefault()
activeIndex = 0
} else if (event.key === "End" && open) {
event.preventDefault()
activeIndex = flatVisibleOptions().length - 1
}
}
function optionIndex(option) {
return flatVisibleOptions().findIndex((item) => item.value === option.value)
}
}
view {
<div
class="wire-next wire-next--color-{color} wire-next--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}"
data-placement="{placement}"
data-search-mode="{searchMode}"
data-remote="{remote ? 'true' : 'false'}"
data-remote-url="{remoteUrl}"
data-remote-query-param="{remoteQueryParam}"
data-remote-debounce="{remoteDebounce}"
data-remote-auto-load="{remoteAutoLoad ? 'true' : 'false'}"
data-infinite="{infinite ? 'true' : 'false'}"
data-page="{page}"
data-wrn-select
data-wrn-combobox
@focusout="if (!event.currentTarget.contains(event.relatedTarget)) { closeDropdown() }"
>
<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>
<input
{...attrs}
id="{name}-input"
class="wire-next__select-trigger wire-next__combobox-input"
type="text"
role="combobox"
name="{allowCustomValue ? name : ''}"
value="{inputText()}"
placeholder="{placeholder}"
autocomplete="{autocomplete}"
aria-autocomplete="list"
aria-haspopup="listbox"
aria-expanded="{open}"
aria-controls="{name}-listbox"
aria-labelledby="{name}-label"
aria-invalid="{invalid}"
disabled="{disabled}"
required="{required && allowCustomValue}"
@focus="openDropdown()"
@click="openDropdown()"
@input="updateQuery(event)"
@keydown="handleKeydown(event)"
/>
<button
type="button"
class="wire-next__clear-select"
data-show="clearable && inputText() && !disabled"
aria-label="{clearLabel}"
title="{clearLabel}"
@click="clearValue(event)"
>
<span class="icon-[lucide--x]" aria-hidden="true"></span>
</button>
<button
type="button"
class="wire-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>
</button>
</div>
<div
class="wire-next__select-dropdown {fixed ? 'wire-next__select-dropdown--fixed' : ''}"
data-show="shouldShowDropdown()"
style="{shouldShowDropdown() ? '' : 'display: none'}"
>
<div
class="wire-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>
{loadingLabel}
</div>
<div
id="{name}-listbox"
class="wire-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>
{#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' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
aria-selected="{isSelected(option)}"
disabled="{!canSelect(option)}"
@mouseenter="activeIndex = optionIndex(option)"
@mousedown="event.preventDefault()"
@click="chooseOption(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}
<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>
</button>
{/each}
</div>
{/if}
{/each}
{#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' : ''}"
data-option-value="{option.value}"
data-show="matches(option)"
role="option"
aria-selected="{isSelected(option)}"
disabled="{!canSelect(option)}"
@mouseenter="activeIndex = optionIndex(option)"
@mousedown="event.preventDefault()"
@click="chooseOption(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}
<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>
</button>
{/each}
</div>
<button
type="button"
class="wire-next__load-more"
data-wrn-select-load-more
data-show="infinite && hasMore"
>{loadMoreLabel}</button>
</div>
{#if !allowCustomValue}
<input
type="hidden"
name="{name}"
value="{selectedValue}"
required="{required}"
data-error="{name}"
aria-describedby="{validationMessage ? `${name}-validation` : helpText ? `${name}-help` : ''}"
/>
{/if}
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
data-error="{name}"
>{validationMessage}</small>
</div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Confetti {
props {
size = "default"
color = "primary"
title = "Confetti"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--confetti wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component ContextMenu {
props {
size = "default"
color = "primary"
title = "Context Menu"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
class = ""
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--context-menu wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
}
}
+20
View File
@@ -0,0 +1,20 @@
component CopyMarkup {
props {
size = "default"
color = "primary"
label = "Copy Markup"
name = ""
value = ""
placeholder = ""
type = "text"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--copy-markup {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
@@ -0,0 +1,13 @@
component CustomScrollbar {
props {
size = "default"
color = "primary"
columns = 2
gap = "md"
maxWidth = "xl"
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--custom-scrollbar wire-next--gap-{gap} wire-next--columns-{columns} wire-next--max-{maxWidth} {class}"><slot /></div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component DataMap {
props {
size = "default"
color = "primary"
title = "Data Map"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--data-map wire-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}
<slot />
</section>
}
}
+14
View File
@@ -0,0 +1,14 @@
component DataTable {
props {
size = "default"
color = "primary"
caption = "Data Table"
columns = []
rows = []
striped = true
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--table {class}"><table><caption>{caption}</caption><thead><tr>{#each columns as column}<th>{column.label}</th>{/each}</tr></thead><tbody>{#each rows as row}<tr>{#each columns as column}<td>{row[column.key]}</td>{/each}</tr>{/each}</tbody></table><slot /></div>
}
}
+20
View File
@@ -0,0 +1,20 @@
component DatePicker {
props {
size = "default"
color = "primary"
label = "Date Picker"
name = ""
value = ""
placeholder = ""
type = "date"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--date-picker {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+19
View File
@@ -0,0 +1,19 @@
component DeviceFrame {
props {
size = "default"
color = "primary"
title = "Device Frame"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--device-frame wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component DragAndDrop {
props {
size = "default"
color = "primary"
title = "Drag And Drop"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--drag-and-drop wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Drawer {
props {
size = "default"
color = "primary"
title = "Drawer"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
class = ""
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--drawer wire-next--placement-{placement} {class}" role="dialog" aria-modal="true" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Dropdown {
props {
size = "default"
color = "primary"
title = "Dropdown"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
class = ""
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--dropdown wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
}
}
+20
View File
@@ -0,0 +1,20 @@
component FileInput {
props {
size = "default"
color = "primary"
label = "File Input"
name = ""
value = ""
placeholder = ""
type = "file"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--file-input {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+19
View File
@@ -0,0 +1,19 @@
component FileUpload {
props {
size = "default"
color = "primary"
title = "File Upload"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--file-upload wire-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}
<slot />
</section>
}
}
@@ -0,0 +1,17 @@
component FileUploadProgress {
props {
size = "default"
color = "primary"
label = "Progress"
value = 50
max = 100
showValue = true
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--file-upload-progress {class}">
<div class="wire-next__row"><span>{label}</span>{#if showValue}<strong>{value}%</strong>{/if}</div>
<progress value="{value}" max="{max}"></progress>
</div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Image {
props {
size = "default"
color = "primary"
src = ""
alt = ""
width = ""
height = ""
loading = "lazy"
rounded = false
class = ""
}
view {
<figure class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--image {rounded ? 'wire-next--rounded' : ''} {class}">
<img src="{src}" alt="{alt}" width="{width}" height="{height}" loading="{loading}" />
<slot />
</figure>
}
}
+20
View File
@@ -0,0 +1,20 @@
component InputGroup {
props {
size = "default"
color = "primary"
label = "Input Group"
name = ""
value = ""
placeholder = ""
type = "text"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--input-group {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+20
View File
@@ -0,0 +1,20 @@
component InputNumber {
props {
size = "default"
color = "primary"
label = "Input Number"
name = ""
value = ""
placeholder = ""
type = "number"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--input-number {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+9
View File
@@ -0,0 +1,9 @@
component Kbd {
props {
size = "default"
color = "primary"
label = "⌘ K"
class = ""
}
view { <kbd class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--kbd {class}"><slot>{label}</slot></kbd> }
}
+13
View File
@@ -0,0 +1,13 @@
component LayoutSplitter {
props {
size = "default"
color = "primary"
columns = 2
gap = "md"
maxWidth = "xl"
class = ""
}
view {
<div class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--layout-splitter wire-next--gap-{gap} wire-next--columns-{columns} wire-next--max-{maxWidth} {class}"><slot /></div>
}
}
@@ -0,0 +1,19 @@
component LegendIndicator {
props {
size = "default"
color = "primary"
title = "Legend Indicator"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--legend-indicator wire-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}
<slot />
</section>
}
}
+15
View File
@@ -0,0 +1,15 @@
component Link {
props {
size = "default"
color = "primary"
label = "Link"
href = "#"
target = ""
rel = ""
external = false
class = ""
}
view {
<a {...attrs} href="{href}" target="{target}" rel="{rel}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--link {class}"><slot>{label}</slot>{#if external}<span aria-hidden="true">↗</span>{/if}</a>
}
}
+19
View File
@@ -0,0 +1,19 @@
component List {
props {
size = "default"
color = "primary"
title = "List"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--list wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component ListGroup {
props {
size = "default"
color = "primary"
title = "List Group"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--list-group wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Map {
props {
size = "default"
color = "primary"
title = "Map"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--map wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Marquee {
props {
size = "default"
color = "primary"
title = "Marquee"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--marquee wire-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}
<slot />
</section>
}
}
+17
View File
@@ -0,0 +1,17 @@
component MegaMenu {
props {
size = "default"
color = "primary"
label = "Mega Menu"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--mega-menu wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Modal {
props {
size = "default"
color = "primary"
title = "Modal"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
class = ""
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--modal wire-next--placement-{placement} {class}" role="dialog" aria-modal="true" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Nav {
props {
size = "default"
color = "primary"
label = "Nav"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--nav wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Navbar {
props {
size = "default"
color = "primary"
label = "Navbar"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--navbar wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Pagination {
props {
size = "default"
color = "primary"
label = "Pagination"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--pagination wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+114
View File
@@ -0,0 +1,114 @@
component PinInput {
props {
size = "default"
color = "primary"
label = "Verification code"
name = "pin"
value = ""
length = 4
pattern = "[0-9]"
type = "text"
inputMode = "numeric"
placeholder = "○"
autocomplete = "one-time-code"
masked = false
disabled = false
readonly = false
required = false
autoFocus = false
autoSubmit = false
allowPaste = true
clearable = true
clearLabel = "Clear code"
separator = ""
groupSize = 0
helpText = ""
invalid = false
validationMessage = ""
class = ""
@event input = function
@event change = function
@event complete = function
@event paste = function
@event clear = function
@event error = function
}
functions {
function inputIndexes() {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].slice(
0,
Math.max(1, Math.min(12, Number(length)))
)
}
}
view {
<fieldset
{...attrs}
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--pin-input {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
data-wrn-pin-input
data-length="{length}"
data-pattern="{pattern}"
data-allow-paste="{allowPaste ? 'true' : 'false'}"
data-auto-submit="{autoSubmit ? 'true' : 'false'}"
data-value="{value}"
disabled="{disabled}"
aria-describedby="{validationMessage ? `${name}-validation` : helpText ? `${name}-help` : ''}"
>
<div class="wire-next__pin-heading">
<legend>{label}</legend>
<button
type="button"
class="wire-next__pin-clear"
data-pin-clear
aria-label="{clearLabel}"
title="{clearLabel}"
style="{clearable && value ? '' : 'display: none'}"
>
<span class="icon-[lucide--rotate-ccw]" aria-hidden="true"></span>
<span>{clearLabel}</span>
</button>
</div>
<div class="wire-next__pin-cells" role="group" aria-label="{label}">
{#each inputIndexes() as index}
<input
data-pin-cell
data-pin-index="{index}"
id="{name}-{index}"
type="{masked ? 'password' : type}"
inputmode="{inputMode}"
maxlength="1"
value="{value[index] || ''}"
placeholder="{placeholder}"
autocomplete="{index === 0 ? autocomplete : 'off'}"
aria-label="{label} digit {index + 1} of {length}"
aria-required="{required ? 'true' : 'false'}"
readonly="{readonly}"
disabled="{disabled}"
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>
{/if}
{/each}
</div>
<input
type="hidden"
name="{name}"
value="{value}"
required="{required}"
data-pin-value
/>
{#if helpText && !validationMessage}<small id="{name}-help">{helpText}</small>{/if}
<small
id="{name}-validation"
class="wire-next__validation"
data-error="{name}"
>{validationMessage}</small>
</fieldset>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Popover {
props {
size = "default"
color = "primary"
title = "Popover"
description = ""
open = false
placement = "bottom"
closeLabel = "Close"
class = ""
}
view {
<div data-show="{open}" class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--popover wire-next--placement-{placement} {class}" role="region" aria-modal="false" aria-label="{title}">
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
{#if description}<p>{description}</p>{/if}
<slot />
</div>
}
}
+20
View File
@@ -0,0 +1,20 @@
component RangeSlider {
props {
size = "default"
color = "primary"
label = "Range Slider"
name = ""
value = ""
placeholder = ""
type = "range"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--range-slider {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Rating {
props {
size = "default"
color = "primary"
title = "Rating"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--rating wire-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}
<slot />
</section>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Scrollspy {
props {
size = "default"
color = "primary"
label = "Scrollspy"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--scrollspy wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+20
View File
@@ -0,0 +1,20 @@
component SearchBox {
props {
size = "default"
color = "primary"
label = "Search Box"
name = ""
value = ""
placeholder = ""
type = "search"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--search-box {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Sidebar {
props {
size = "default"
color = "primary"
label = "Sidebar"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--sidebar wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Stepper {
props {
size = "default"
color = "primary"
label = "Stepper"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--stepper wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+215
View File
@@ -0,0 +1,215 @@
component StrongPassword {
props {
size = "default"
color = "primary"
label = "Password"
name = "password"
value = ""
placeholder = "Create a strong password"
autocomplete = "new-password"
minLength = 8
specialCharactersSet = "!@#$%^&*()_+-=[]{}|;:,.<>?"
requireLowercase = true
requireUppercase = true
requireNumber = true
requireSpecialCharacter = true
showRequirements = true
presentation = "inline"
hintText = "Use a unique password you do not use elsewhere."
emptyLabel = "Enter a password"
weakLabel = "Weak"
fairLabel = "Fair"
goodLabel = "Good"
strongLabel = "Strong"
disabled = false
readonly = false
required = false
invalid = false
validationMessage = ""
class = ""
@event input = function
@event change = function
@event strength = function
}
state password = value
state detailsOpen = false
functions {
function hasMinimumLength() {
return password.length >= Number(minLength)
}
function hasLowercase() {
return password !== password.toUpperCase()
}
function hasUppercase() {
return password !== password.toLowerCase()
}
function hasNumber() {
return containsCharacterAt("0123456789", 0)
}
function hasSpecialCharacter() {
return containsCharacterAt(specialCharactersSet, 0)
}
function containsCharacterAt(characters, index) {
if (index >= characters.length) {
return false
}
if (password.includes(characters[index])) {
return true
}
return containsCharacterAt(characters, index + 1)
}
function score() {
return (hasMinimumLength() ? 1 : 0) + (requireLowercase && hasLowercase() ? 1 : 0) + (requireUppercase && hasUppercase() ? 1 : 0) + (requireNumber && hasNumber() ? 1 : 0) + (requireSpecialCharacter && hasSpecialCharacter() ? 1 : 0)
}
function maximumScore() {
return 1 + (requireLowercase ? 1 : 0) + (requireUppercase ? 1 : 0) + (requireNumber ? 1 : 0) + (requireSpecialCharacter ? 1 : 0)
}
function strengthPercent() {
return Math.round(score() / maximumScore() * 100)
}
function strengthLevel() {
if (!password) {
return "empty"
}
if (strengthPercent() <= 25) {
return "weak"
}
if (strengthPercent() <= 50) {
return "fair"
}
if (strengthPercent() < 100) {
return "good"
}
return "strong"
}
function strengthLabel() {
if (strengthLevel() === "weak") {
return weakLabel
}
if (strengthLevel() === "fair") {
return fairLabel
}
if (strengthLevel() === "good") {
return goodLabel
}
if (strengthLevel() === "strong") {
return strongLabel
}
return emptyLabel
}
}
view {
<div
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--strong-password wire-next--strong-password-{presentation} {invalid ? 'wire-next--invalid' : ''} {disabled ? 'wire-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">
<input
{...attrs}
id="{name}-strong-password"
type="password"
name="{name}"
value="{password}"
placeholder="{placeholder}"
autocomplete="{autocomplete}"
minlength="{minLength}"
disabled="{disabled}"
readonly="{readonly}"
required="{required}"
aria-invalid="{invalid ? 'true' : 'false'}"
aria-describedby="{validationMessage ? name + '-validation' : hintText ? name + '-hint' : ''}"
@focus="detailsOpen = true"
@blur="detailsOpen = false"
@input="password = event.target.value; $emit('input', { value: password, score: score(), maximumScore: maximumScore(), percent: strengthPercent(), level: strengthLevel() }); $emit('strength', { value: password, score: score(), maximumScore: maximumScore(), percent: strengthPercent(), level: strengthLevel() })"
@change="$emit('change', { value: password, score: score(), maximumScore: maximumScore(), percent: strengthPercent(), level: strengthLevel() })"
/>
{#if presentation === "popover"}
<div
class="wire-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">
<span>Password strength</span>
<strong>{password ? strengthLabel() : emptyLabel}</strong>
</div>
<div
class="wire-next__strong-password-meter"
role="progressbar"
aria-label="Password strength"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="{password ? strengthPercent() : 0}"
>
<span style="width: {password ? strengthPercent() : 0}%"></span>
</div>
{#if showRequirements}
<ul class="wire-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}
</ul>
{/if}
{#if hintText}<small id="{name}-hint">{hintText}</small>{/if}
</div>
</div>
{/if}
</div>
{#if presentation !== "popover"}
<div class="wire-next__strong-password-details">
<div class="wire-next__strong-password-summary">
<span>Password strength</span>
<strong>{password ? strengthLabel() : emptyLabel}</strong>
</div>
<div
class="wire-next__strong-password-meter"
role="progressbar"
aria-label="Password strength"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="{password ? strengthPercent() : 0}"
>
<span style="width: {password ? strengthPercent() : 0}%"></span>
</div>
{#if showRequirements}
<ul class="wire-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}
</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>
</div>
}
}
+19
View File
@@ -0,0 +1,19 @@
component StyledIcon {
props {
size = "default"
color = "primary"
title = "Styled Icon"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--styled-icon wire-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}
<slot />
</section>
}
}
+17
View File
@@ -0,0 +1,17 @@
component Tabs {
props {
size = "default"
color = "primary"
label = "Tabs"
items = []
active = ""
orientation = "horizontal"
class = ""
}
view {
<nav class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--tabs wire-next--{orientation} {class}" aria-label="{label}">
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
<slot />
</nav>
}
}
+20
View File
@@ -0,0 +1,20 @@
component TimePicker {
props {
size = "default"
color = "primary"
label = "Time Picker"
name = ""
value = ""
placeholder = ""
type = "time"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--time-picker {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Timeline {
props {
size = "default"
color = "primary"
title = "Timeline"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--timeline wire-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}
<slot />
</section>
}
}
+19
View File
@@ -0,0 +1,19 @@
component Toast {
props {
size = "default"
color = "primary"
title = "Toast"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--toast wire-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}
<slot />
</section>
}
}
@@ -0,0 +1,19 @@
component ToastNotifications {
props {
size = "default"
color = "primary"
title = "Toast Notifications"
description = ""
items = []
variant = "default"
class = ""
}
view {
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--toast-notifications wire-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}
<slot />
</section>
}
}
+20
View File
@@ -0,0 +1,20 @@
component ToggleCount {
props {
size = "default"
color = "primary"
label = "Toggle Count"
name = ""
value = ""
placeholder = ""
type = "text"
min = ""
max = ""
step = ""
disabled = false
required = false
class = ""
}
view {
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--toggle-count {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
}
}

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