chore(release): prepare 0.8.6
Bumps all 47 packages, the root manifest and the VS Code extension to 0.8.6, and rebuilds the editor compiler, language server and extension bundles that embed the version. The release carries the output delivery fix: camelCase outputs now reach parent bindings, and 18 components emit through output.* instead of hand-built CustomEvents. See the 0.8.6 migration entry for what changes for consumers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/authz — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -41,8 +41,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/db": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/db": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
+18
-18
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/cli — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -44,23 +44,23 @@
|
||||
"wrnexus": "./dist/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/router": "^0.8.5",
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/styles": "^0.8.5",
|
||||
"@wrnexus/dev-server": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5",
|
||||
"@wrnexus/i18n": "^0.8.5",
|
||||
"@wrnexus/mcp": "^0.8.5",
|
||||
"@wrnexus/playground": "^0.8.5",
|
||||
"@wrnexus/db": "^0.8.5",
|
||||
"@wrnexus/authz": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/syntax": "^0.8.5",
|
||||
"@wrnexus/typecheck": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/router": "^0.8.6",
|
||||
"@wrnexus/csr": "^0.8.6",
|
||||
"@wrnexus/compiler": "^0.8.6",
|
||||
"@wrnexus/styles": "^0.8.6",
|
||||
"@wrnexus/dev-server": "^0.8.6",
|
||||
"@wrnexus/ui": "^0.8.6",
|
||||
"@wrnexus/validation": "^0.8.6",
|
||||
"@wrnexus/i18n": "^0.8.6",
|
||||
"@wrnexus/mcp": "^0.8.6",
|
||||
"@wrnexus/playground": "^0.8.6",
|
||||
"@wrnexus/db": "^0.8.6",
|
||||
"@wrnexus/authz": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6",
|
||||
"@wrnexus/syntax": "^0.8.6",
|
||||
"@wrnexus/typecheck": "^0.8.6",
|
||||
"@wrnexus/security": "^0.8.6",
|
||||
"selfsigned": "^5.5.0"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/compiler — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,10 +37,10 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/syntax": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5"
|
||||
"@wrnexus/csr": "^0.8.6",
|
||||
"@wrnexus/syntax": "^0.8.6",
|
||||
"@wrnexus/store": "^0.8.6",
|
||||
"@wrnexus/validation": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/core — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/csr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Typed database drivers, migrations, instrumentation, repositories, pagination, and transaction helpers.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-server — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -41,27 +41,27 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "^0.8.5",
|
||||
"@wrnexus/rpc": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/dev-toolbar": "^0.8.5",
|
||||
"@wrnexus/router": "^0.8.5",
|
||||
"@wrnexus/ssr": "^0.8.5",
|
||||
"@wrnexus/csr": "^0.8.5",
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/styles": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5",
|
||||
"@wrnexus/validation": "^0.8.5",
|
||||
"@wrnexus/i18n": "^0.8.5",
|
||||
"@wrnexus/db": "^0.8.5",
|
||||
"@wrnexus/pubsub": "^0.8.5",
|
||||
"@wrnexus/uploader": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5",
|
||||
"@wrnexus/observability": "^0.8.5",
|
||||
"@wrnexus/cache": "^0.8.5",
|
||||
"@wrnexus/pwa": "^0.8.5"
|
||||
"@wrnexus/authz": "^0.8.6",
|
||||
"@wrnexus/rpc": "^0.8.6",
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/dev-toolbar": "^0.8.6",
|
||||
"@wrnexus/router": "^0.8.6",
|
||||
"@wrnexus/ssr": "^0.8.6",
|
||||
"@wrnexus/csr": "^0.8.6",
|
||||
"@wrnexus/compiler": "^0.8.6",
|
||||
"@wrnexus/styles": "^0.8.6",
|
||||
"@wrnexus/ui": "^0.8.6",
|
||||
"@wrnexus/validation": "^0.8.6",
|
||||
"@wrnexus/i18n": "^0.8.6",
|
||||
"@wrnexus/db": "^0.8.6",
|
||||
"@wrnexus/pubsub": "^0.8.6",
|
||||
"@wrnexus/uploader": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6",
|
||||
"@wrnexus/store": "^0.8.6",
|
||||
"@wrnexus/security": "^0.8.6",
|
||||
"@wrnexus/observability": "^0.8.6",
|
||||
"@wrnexus/cache": "^0.8.6",
|
||||
"@wrnexus/pwa": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/dev-toolbar — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Authenticated encryption, key rotation, hashing, and optional application-layer encrypted HTTP envelopes.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Locale loading, fallback resolution, SSR/browser translations, formatters, and WRNexusJS language components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6",
|
||||
"@wrnexus/ui": "^0.8.6"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "HS256 JSON Web Tokens, key rotation, access/refresh helpers, scopes, cookies, and auth middleware.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/mobile — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "^0.8.5"
|
||||
"@wrnexus/native": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/native — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/oauth — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/jwt": "^0.8.5"
|
||||
"@wrnexus/jwt": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/plugin — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -49,7 +49,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "^0.8.5"
|
||||
"@wrnexus/syntax": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/pubsub — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/queue — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/reactive — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/router — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,8 +37,8 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/compiler": "^0.8.6",
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ssr — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -45,9 +45,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/store": "^0.8.5",
|
||||
"@wrnexus/security": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/store": "^0.8.6",
|
||||
"@wrnexus/security": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/styles — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -37,9 +37,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/uploader": "^0.8.5",
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5"
|
||||
"@wrnexus/uploader": "^0.8.6",
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/syntax — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/test — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/tracking — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
|
||||
+33
-33
@@ -229,7 +229,7 @@ Present structured responsive linked or status items with icons, descriptions, a
|
||||
- Mount: `data-component="List"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "List"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
- Outputs: `select({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
### ListGroup
|
||||
|
||||
@@ -247,7 +247,7 @@ Continuously present responsive labels, partners, notices, or capabilities with
|
||||
- Mount: `data-component="Marquee"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Marquee"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
- Outputs: `pause({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `resume({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
### Progress
|
||||
|
||||
@@ -301,7 +301,7 @@ Present responsive chronological activity, milestones, or workflow status with r
|
||||
- Mount: `data-component="Timeline"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Timeline"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
- Outputs: `select({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
### Toast
|
||||
|
||||
@@ -469,7 +469,7 @@ Provide an accessible responsive search field with labels, validation states, si
|
||||
- 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
|
||||
- Outputs: None
|
||||
- Outputs: `search({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `clear({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
### Select
|
||||
|
||||
@@ -588,7 +588,7 @@ Present responsive location information and markers with map-ready metadata and
|
||||
- Mount: `data-component="Map"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Map"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
- Outputs: `markerClick({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `zoom({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
|
||||
### ToastNotifications
|
||||
|
||||
@@ -624,7 +624,7 @@ Create responsive balanced content columns with configurable count, gap, density
|
||||
Constrain and align page content with responsive gutters and compact, wide, or full width options.
|
||||
|
||||
- Mount: `data-component="Container"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `centered: boolean = true`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
|
||||
@@ -633,16 +633,16 @@ Constrain and align page content with responsive gutters and compact, wide, or f
|
||||
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 = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `axis: string = "vertical"`, `thickness: number = 8`, `maxHeight: string = "20rem"`, `radius: string = "999px"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `scroll({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
|
||||
- Outputs: None
|
||||
|
||||
### Divider
|
||||
|
||||
Separate related horizontal or vertical content with optional labels, sizes, and semantic colors.
|
||||
|
||||
- Mount: `data-component="Divider"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = ""`, `orientation: string = "horizontal"`, `variant: string = "solid"`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: None
|
||||
|
||||
@@ -669,7 +669,7 @@ Render structured responsive footer navigation, pre and post content, copyright
|
||||
Arrange arbitrary content in a responsive configurable CSS grid with stable columns, gaps, alignment, and width.
|
||||
|
||||
- Mount: `data-component="Grid"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `columns: number = 2`, `gap: string = "md"`, `maxWidth: string = "xl"`, `minItemWidth: string = ""`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
|
||||
@@ -678,7 +678,7 @@ Arrange arbitrary content in a responsive configurable CSS grid with stable colu
|
||||
Render a responsive image with explicit dimensions, loading behavior, alternative text, sizing, and rounded treatment.
|
||||
|
||||
- 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 = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `src: string = ""`, `alt: string = ""`, `width: string = ""`, `height: string = ""`, `loading: string = "lazy"`, `fit: string = "cover"`, `rounded: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
|
||||
@@ -687,7 +687,7 @@ Render a responsive image with explicit dimensions, loading behavior, alternativ
|
||||
Theme-aware, responsive kbd component.
|
||||
|
||||
- Mount: `data-component="Kbd"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "⌘ K"`, `class: string = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "K"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
|
||||
@@ -696,16 +696,16 @@ Theme-aware, responsive kbd component.
|
||||
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`
|
||||
- Outputs: `resizeStart({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `resize({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `resizeEnd({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
|
||||
- Props: `color: string = "primary"`, `size: number = 50`, `orientation: string = "horizontal"`, `minSize: number = 15`, `step: number = 5`, `label: string = "Resize panels"`, `class: string = ""`
|
||||
- Slots: `start`, `end`, `default`
|
||||
- Outputs: `sizeChange({ size: number })`
|
||||
|
||||
### Link
|
||||
|
||||
Render an accessible internal or external link with target, relation, size, color, and public focus or click events.
|
||||
|
||||
- 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 = ""`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Link"`, `href: string = "#"`, `target: string = ""`, `rel: string = ""`, `underline: string = "hover"`, `external: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
|
||||
@@ -862,18 +862,18 @@ Show responsive hierarchical navigation with home support, separators, current-p
|
||||
Theme-aware, responsive mega menu component.
|
||||
|
||||
- Mount: `data-component="MegaMenu"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Mega Menu"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Menu"`, `icon: string = ""`, `columns: unknown[] = []`, `footer: string = ""`, `defaultOpen: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
|
||||
- Outputs: `open({ sourceEvent: Event })`, `close({ reason: string })`, `select({ item: object; value: string })`
|
||||
|
||||
### Nav
|
||||
|
||||
Theme-aware, responsive nav component.
|
||||
|
||||
- Mount: `data-component="Nav"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Nav"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `label: string = "Main"`, `collapsible: boolean = true`, `toggleLabel: string = "Menu"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
|
||||
- Outputs: `select({ item: object; value: string })`
|
||||
|
||||
### Navbar
|
||||
|
||||
@@ -889,45 +889,45 @@ Theme-aware, responsive navbar component.
|
||||
Theme-aware, responsive pagination component.
|
||||
|
||||
- Mount: `data-component="Pagination"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Pagination"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `page: number = 1`, `pageSize: number = 10`, `total: number = 0`, `variant: string = "compact"`, `siblingCount: number = 1`, `showSummary: boolean = true`, `label: string = "Pagination"`, `previousLabel: string = "Previous"`, `nextLabel: string = "Next"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `previous({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `next({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
- Outputs: `change({ page: number; pageSize: number })`, `previous({ page: number })`, `next({ page: number })`
|
||||
|
||||
### Scrollspy
|
||||
|
||||
Theme-aware, responsive scrollspy component.
|
||||
|
||||
- Mount: `data-component="Scrollspy"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Scrollspy"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `label: string = "On this page"`, `heading: string = ""`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`
|
||||
- Outputs: `change({ href: string; label: string })`
|
||||
|
||||
### Sidebar
|
||||
|
||||
Theme-aware, responsive sidebar component.
|
||||
|
||||
- Mount: `data-component="Sidebar"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Sidebar"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `mobileLabel: string = "Open navigation"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `toggle({ open: boolean })`, `open({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ source: string })`, `select({ item: string | number | boolean | null | object; value: string | number | boolean; level: string })`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `label: string = "Sidebar"`, `items: unknown[] = []`, `active: string = ""`, `mobileLabel: string = "Open navigation"`, `drawerTitle: string = "Navigation"`, `class: string = ""`
|
||||
- Slots: `default`, `drawer`
|
||||
- Outputs: `toggle({ open: boolean })`, `open({ sourceEvent: Event })`, `close({ source: string })`, `select({ item: object; value: string; level: number })`
|
||||
|
||||
### Stepper
|
||||
|
||||
Theme-aware, responsive stepper component.
|
||||
|
||||
- Mount: `data-component="Stepper"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Stepper"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `previous({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `next({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `complete({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `steps: unknown[] = []`, `active: number = 0`, `orientation: string = "horizontal"`, `clickable: boolean = false`, `label: string = "Progress"`, `showPanel: boolean = false`, `controls: boolean = false`, `allowSkip: boolean = false`, `nextDisabled: boolean = false`, `backLabel: string = "Back"`, `nextLabel: string = "Next"`, `skipLabel: string = "Skip"`, `finishLabel: string = "Finish"`, `class: string = ""`
|
||||
- Slots: `step-{index}`, `panel-{index}`, `default`
|
||||
- Outputs: `change({ index: number; step: object })`, `back({ index: number; step: object })`, `next({ index: number; step: object })`, `skip({ index: number; step: object })`, `finish({ index: number; step: object })`
|
||||
|
||||
### Tabs
|
||||
|
||||
Switch between related responsive content panels with horizontal or vertical orientation and selection events.
|
||||
|
||||
- Mount: `data-component="Tabs"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Tabs"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: None
|
||||
- Props: `color: string = "primary"`, `size: string = "default"`, `items: unknown[] = []`, `active: string = ""`, `orientation: string = "horizontal"`, `mode: string = "client"`, `param: string = "tab"`, `label: string = "Tabs"`, `class: string = ""`
|
||||
- Slots: `panel-{valueOf(item, index)}`, `default`
|
||||
- Outputs: `change({ value: string; item: object; index: number })`, `select({ value: string; item: object; index: number })`
|
||||
|
||||
## Overlays
|
||||
|
||||
|
||||
@@ -4153,6 +4153,13 @@
|
||||
"default": "\"xl\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "centered",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -4443,13 +4450,6 @@
|
||||
"category": "layout",
|
||||
"purpose": "Theme-aware, responsive custom scrollbar component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -4458,24 +4458,38 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "columns",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "axis",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"vertical\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "thickness",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "2",
|
||||
"default": "8",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gap",
|
||||
"name": "maxHeight",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"default": "\"20rem\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "maxWidth",
|
||||
"name": "radius",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"xl\"",
|
||||
"default": "\"999px\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -4487,13 +4501,8 @@
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "scroll",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
}
|
||||
],
|
||||
"events": ["scroll"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"source": "components/CustomScrollbar.wrn"
|
||||
},
|
||||
{
|
||||
@@ -5192,6 +5201,13 @@
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"solid\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -6548,6 +6564,13 @@
|
||||
"default": "\"xl\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "minItemWidth",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -6962,6 +6985,13 @@
|
||||
"default": "\"lazy\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "fit",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"cover\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "rounded",
|
||||
"type": "boolean",
|
||||
@@ -7750,7 +7780,7 @@
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"⌘ K\"",
|
||||
"default": "\"K\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -7772,13 +7802,6 @@
|
||||
"category": "layout",
|
||||
"purpose": "Theme-aware, responsive layout splitter component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -7787,24 +7810,38 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "columns",
|
||||
"name": "size",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "2",
|
||||
"default": "50",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "gap",
|
||||
"name": "orientation",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"md\"",
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "maxWidth",
|
||||
"name": "minSize",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "15",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "step",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "5",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"xl\"",
|
||||
"default": "\"Resize panels\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -7815,22 +7852,14 @@
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"slots": ["start", "end", "default"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "resizeStart",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
},
|
||||
{
|
||||
"name": "resize",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
},
|
||||
{
|
||||
"name": "resizeEnd",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
"name": "sizeChange",
|
||||
"payloadType": "{ size: number }"
|
||||
}
|
||||
],
|
||||
"events": ["resizeStart", "resize", "resizeEnd"],
|
||||
"events": ["sizeChange"],
|
||||
"source": "components/LayoutSplitter.wrn"
|
||||
},
|
||||
{
|
||||
@@ -7951,6 +7980,13 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "underline",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"hover\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "external",
|
||||
"type": "boolean",
|
||||
@@ -8028,8 +8064,13 @@
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
}
|
||||
],
|
||||
"events": ["select"],
|
||||
"source": "components/List.wrn"
|
||||
},
|
||||
{
|
||||
@@ -8159,8 +8200,21 @@
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "markerClick",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
},
|
||||
{
|
||||
"name": "zoom",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
}
|
||||
],
|
||||
"events": ["markerClick", "select", "zoom"],
|
||||
"source": "components/Map.wrn"
|
||||
},
|
||||
{
|
||||
@@ -8316,8 +8370,17 @@
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "pause",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
},
|
||||
{
|
||||
"name": "resume",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
}
|
||||
],
|
||||
"events": ["pause", "resume"],
|
||||
"source": "components/Marquee.wrn"
|
||||
},
|
||||
{
|
||||
@@ -8326,13 +8389,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive mega menu component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -8340,32 +8396,46 @@
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Mega Menu\"",
|
||||
"default": "\"Menu\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"name": "icon",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "orientation",
|
||||
"name": "columns",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "footer",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"horizontal\"",
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "defaultOpen",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -8380,15 +8450,15 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "open",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ sourceEvent: Event }"
|
||||
},
|
||||
{
|
||||
"name": "close",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ reason: string }"
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
"payloadType": "{ item: object; value: string }"
|
||||
}
|
||||
],
|
||||
"events": ["open", "close", "select"],
|
||||
@@ -8961,13 +9031,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive nav component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -8976,10 +9039,10 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Nav\"",
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -9003,6 +9066,27 @@
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Main\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "collapsible",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "toggleLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Menu\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -9015,14 +9099,10 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
},
|
||||
{
|
||||
"name": "change",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
"payloadType": "{ item: object; value: string }"
|
||||
}
|
||||
],
|
||||
"events": ["select", "change"],
|
||||
"events": ["select"],
|
||||
"source": "components/Nav.wrn"
|
||||
},
|
||||
{
|
||||
@@ -9342,6 +9422,13 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive pagination component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
@@ -9350,10 +9437,45 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"name": "page",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "1",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "pageSize",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "10",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "total",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "0",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"default": "\"compact\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "siblingCount",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "1",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showSummary",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -9364,24 +9486,17 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"type": "unknown[]",
|
||||
"name": "previousLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"default": "\"Previous\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"name": "nextLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "orientation",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"horizontal\"",
|
||||
"default": "\"Next\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -9396,15 +9511,15 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "change",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
"payloadType": "{ page: number; pageSize: number }"
|
||||
},
|
||||
{
|
||||
"name": "previous",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ page: number }"
|
||||
},
|
||||
{
|
||||
"name": "next",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ page: number }"
|
||||
}
|
||||
],
|
||||
"events": ["change", "previous", "next"],
|
||||
@@ -10664,13 +10779,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive scrollspy component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -10679,10 +10787,10 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Scrollspy\"",
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -10700,10 +10808,17 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "orientation",
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"horizontal\"",
|
||||
"default": "\"On this page\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "heading",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -10718,7 +10833,7 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "change",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
"payloadType": "{ href: string; label: string }"
|
||||
}
|
||||
],
|
||||
"events": ["change"],
|
||||
@@ -10823,8 +10938,17 @@
|
||||
}
|
||||
],
|
||||
"slots": [],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "search",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
},
|
||||
{
|
||||
"name": "clear",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
}
|
||||
],
|
||||
"events": ["search", "clear"],
|
||||
"source": "components/SearchBox.wrn"
|
||||
},
|
||||
{
|
||||
@@ -11192,13 +11316,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive sidebar component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -11206,6 +11323,13 @@
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
@@ -11227,13 +11351,6 @@
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "orientation",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mobileLabel",
|
||||
"type": "string",
|
||||
@@ -11241,6 +11358,13 @@
|
||||
"default": "\"Open navigation\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "drawerTitle",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Navigation\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -11249,7 +11373,7 @@
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"slots": ["default", "drawer"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "toggle",
|
||||
@@ -11257,7 +11381,7 @@
|
||||
},
|
||||
{
|
||||
"name": "open",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ sourceEvent: Event }"
|
||||
},
|
||||
{
|
||||
"name": "close",
|
||||
@@ -11265,7 +11389,7 @@
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ item: string | number | boolean | null | object; value: string | number | boolean; level: string }"
|
||||
"payloadType": "{ item: object; value: string; level: number }"
|
||||
}
|
||||
],
|
||||
"events": ["toggle", "open", "close", "select"],
|
||||
@@ -11682,13 +11806,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Theme-aware, responsive stepper component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -11697,14 +11814,14 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Stepper\"",
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"name": "steps",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
@@ -11712,9 +11829,9 @@
|
||||
},
|
||||
{
|
||||
"name": "active",
|
||||
"type": "string",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"default": "0",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -11724,6 +11841,76 @@
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "clickable",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Progress\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showPanel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "controls",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "allowSkip",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "nextDisabled",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "backLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Back\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "nextLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Next\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "skipLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Skip\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "finishLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Finish\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -11732,26 +11919,30 @@
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"slots": ["step-{index}", "panel-{index}", "default"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "change",
|
||||
"payloadType": "{ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null"
|
||||
"payloadType": "{ index: number; step: object }"
|
||||
},
|
||||
{
|
||||
"name": "previous",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"name": "back",
|
||||
"payloadType": "{ index: number; step: object }"
|
||||
},
|
||||
{
|
||||
"name": "next",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ index: number; step: object }"
|
||||
},
|
||||
{
|
||||
"name": "complete",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"name": "skip",
|
||||
"payloadType": "{ index: number; step: object }"
|
||||
},
|
||||
{
|
||||
"name": "finish",
|
||||
"payloadType": "{ index: number; step: object }"
|
||||
}
|
||||
],
|
||||
"events": ["change", "previous", "next", "complete"],
|
||||
"events": ["change", "back", "next", "skip", "finish"],
|
||||
"source": "components/Stepper.wrn"
|
||||
},
|
||||
{
|
||||
@@ -12204,13 +12395,6 @@
|
||||
"category": "navigation",
|
||||
"purpose": "Switch between related responsive content panels with horizontal or vertical orientation and selection events.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
@@ -12219,10 +12403,10 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Tabs\"",
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -12246,6 +12430,27 @@
|
||||
"default": "\"horizontal\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "mode",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"client\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "param",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"tab\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Tabs\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -12254,9 +12459,18 @@
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"slots": ["panel-{valueOf(item, index)}", "default"],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "change",
|
||||
"payloadType": "{ value: string; item: object; index: number }"
|
||||
},
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ value: string; item: object; index: number }"
|
||||
}
|
||||
],
|
||||
"events": ["change", "select"],
|
||||
"source": "components/Tabs.wrn"
|
||||
},
|
||||
{
|
||||
@@ -12856,8 +13070,13 @@
|
||||
}
|
||||
],
|
||||
"slots": ["default"],
|
||||
"outputs": [],
|
||||
"events": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "select",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
}
|
||||
],
|
||||
"events": ["select"],
|
||||
"source": "components/Timeline.wrn"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -49,22 +49,24 @@ component Accordion {
|
||||
return multiple || alwaysOpen
|
||||
}
|
||||
|
||||
client function dispatchAccordionEvent(sourceEvent, eventName, value, item, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-accordion]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent(eventName, true, false, {
|
||||
// Named rather than computed: an output is resolved as a property name, so
|
||||
// output[eventName] would not reach a parent binding.
|
||||
client function dispatchAccordionEvent(sourceEvent, eventName, value, item, payload) {
|
||||
payload = {
|
||||
component: "Accordion",
|
||||
value: value,
|
||||
item: item,
|
||||
open: isOpen(value),
|
||||
openValues: openValues
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
}
|
||||
|
||||
if (eventName === "open") {
|
||||
output.open(payload)
|
||||
} else if (eventName === "close") {
|
||||
output.close(payload)
|
||||
} else {
|
||||
output.change(payload)
|
||||
}
|
||||
}
|
||||
|
||||
client function toggleItem(sourceEvent, value, item, wasOpen) {
|
||||
|
||||
@@ -30,17 +30,13 @@ component AvatarGroup {
|
||||
return items.slice(Number(maxVisible))
|
||||
}
|
||||
|
||||
client function toggleOverflow(sourceEvent, root, customEvent) {
|
||||
client function toggleOverflow() {
|
||||
overflowOpen = !overflowOpen
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-avatar-group]")
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("overflow", true, false, {
|
||||
output.overflow({
|
||||
component: "AvatarGroup",
|
||||
open: overflowOpen,
|
||||
hiddenCount: hiddenMembers().length
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ label: string = "Breadcrumb"
|
||||
<a
|
||||
href='{homeHref || "/"}'
|
||||
class="wire-breadcrumb__link wire-breadcrumb__home"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: { label: homeLabel, href: homeHref || "/", value: "home" }, itemIndex: -1 } }))'
|
||||
@click='output.select({ item: { label: homeLabel, href: homeHref || "/", value: "home" }, itemIndex: -1 })'
|
||||
>
|
||||
{#if homeIcon === "icon-[lucide--house]"}
|
||||
<span class="icon-[lucide--house] wire-breadcrumb__icon" aria-hidden="true"></span>
|
||||
@@ -72,7 +72,7 @@ label: string = "Breadcrumb"
|
||||
target='{item.target || ""}'
|
||||
rel='{item.external ? "noopener noreferrer" : (item.rel || "")}'
|
||||
class="wire-breadcrumb__link"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: item, itemIndex: itemIndex } }))'
|
||||
@click='output.select({ item: item, itemIndex: itemIndex })'
|
||||
>
|
||||
{#if item.icon}
|
||||
<span class='{item.icon} wire-breadcrumb__icon' aria-hidden="true"></span>
|
||||
@@ -87,7 +87,7 @@ label: string = "Breadcrumb"
|
||||
{:else}
|
||||
<span
|
||||
class="wire-breadcrumb__current"
|
||||
aria-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "page" : ""}'
|
||||
aria-current='{item.active || item.current || (active && active === (item.value || item.label || item.title)) || (!active && itemIndex === items.length - 1) ? "page" : "false"}'
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
>
|
||||
{#if item.icon}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// Columns -- a simple multi-column split for page content.
|
||||
//
|
||||
// <Columns columns={2} gap="lg">...</Columns>
|
||||
//
|
||||
// Columns and Grid overlap deliberately: Columns is the coarse two or three
|
||||
// way split of a page, and its gaps are wider for that reason. Grid is the one
|
||||
// to reach for when the items are cards and the count matters.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Columns {
|
||||
props {
|
||||
size: string = "default"
|
||||
@@ -8,28 +18,92 @@ component Columns {
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function columnCount() {
|
||||
var value = Number(columns)
|
||||
if (!value || value < 1) {
|
||||
return 1
|
||||
}
|
||||
return Math.min(4, value)
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Columns"
|
||||
class='wire-columns {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-columns='{columns}'
|
||||
data-gap='{gap}'
|
||||
class='grid w-full grid-cols-1 items-start {class}'
|
||||
class:max-w-3xl='maxWidth === "md"'
|
||||
class:max-w-5xl='maxWidth === "lg"'
|
||||
class:max-w-7xl='maxWidth === "xl"'
|
||||
class:max-w-screen-2xl='maxWidth === "2xl"'
|
||||
class:max-w-none='maxWidth === "full"'
|
||||
class:md:grid-cols-2='columns >= 2'
|
||||
class:lg:grid-cols-3='columns === 3'
|
||||
class:lg:grid-cols-4='columns === 4'
|
||||
class:gap-3='gap === "sm"'
|
||||
class:gap-6='gap === "md"'
|
||||
class:gap-10='gap === "lg"'
|
||||
class:gap-14='gap === "xl"'
|
||||
data-max-width='{maxWidth}'
|
||||
data-columns='{columnCount()}'
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-columns {
|
||||
--columns-gap: 1.5rem;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: var(--columns-gap);
|
||||
grid-template-columns: 1fr;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.wire-columns[data-max-width="md"] {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-max-width="lg"] {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-max-width="xl"] {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-max-width="2xl"] {
|
||||
max-width: 96rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-max-width="full"] {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wire-columns[data-gap="sm"] {
|
||||
--columns-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-gap="lg"] {
|
||||
--columns-gap: 2.5rem;
|
||||
}
|
||||
|
||||
.wire-columns[data-gap="xl"] {
|
||||
--columns-gap: 3.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Splits at the tablet breakpoint rather than the phone one: a page split
|
||||
* is worth keeping single-column for longer than a card deck is.
|
||||
*/
|
||||
@media (min-width: 768px) {
|
||||
.wire-columns:not([data-columns="1"]) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wire-columns[data-columns="3"] {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-columns[data-columns="4"] {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,122 @@
|
||||
// CustomScrollbar -- a scrolling region with a themed scrollbar.
|
||||
//
|
||||
// <CustomScrollbar maxHeight="20rem">...long content...</CustomScrollbar>
|
||||
//
|
||||
// Scrollbar appearance is CSS, not script: scrollbar-width and scrollbar-color
|
||||
// are the standard properties, and the ::-webkit-scrollbar rules cover the
|
||||
// browsers that still need them.
|
||||
//
|
||||
// This component used to declare a scroll output it never emitted, and its
|
||||
// props were columns, gap and maxWidth copied from a grid scaffold. The output
|
||||
// is removed rather than left unimplemented -- a caller can listen for a plain
|
||||
// scroll event on the element, which is what it would have been anyway.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component CustomScrollbar {
|
||||
outputs {
|
||||
scroll(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
columns: number = 2
|
||||
gap: string = "md"
|
||||
maxWidth: string = "xl"
|
||||
size: string = "default"
|
||||
axis: string = "vertical"
|
||||
// Track thickness in pixels. Clamped, because it arrives as an attribute
|
||||
// and a scrollbar wider than the content is not useful to anyone.
|
||||
thickness: number = 8
|
||||
maxHeight: string = "20rem"
|
||||
radius: string = "999px"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function trackSize() {
|
||||
var value = Number(thickness)
|
||||
if (!value || value < 2) {
|
||||
return 8
|
||||
}
|
||||
return Math.min(24, value)
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="CustomScrollbar"
|
||||
class='wire-scrollbar {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-axis='{axis}'
|
||||
style='--scrollbar-thickness:{trackSize()}px;--scrollbar-radius:{radius};max-height:{maxHeight};'
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-scrollbar {
|
||||
--scrollbar-accent: var(--wire-color-primary);
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
/*
|
||||
* The standard properties. Firefox and current Chrome honour these; the
|
||||
* webkit rules below are only for the engines that still ignore them.
|
||||
*/
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: color-mix(in srgb, var(--scrollbar-accent) 55%, transparent) transparent;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-color="secondary"] {
|
||||
--scrollbar-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-color="success"] {
|
||||
--scrollbar-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-color="danger"] {
|
||||
--scrollbar-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-color="info"] {
|
||||
--scrollbar-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-axis="vertical"] {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.wire-scrollbar[data-axis="horizontal"] {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.wire-scrollbar::-webkit-scrollbar {
|
||||
width: var(--scrollbar-thickness, 8px);
|
||||
height: var(--scrollbar-thickness, 8px);
|
||||
}
|
||||
|
||||
.wire-scrollbar::-webkit-scrollbar-track {
|
||||
background: var(--wire-color-surface-soft);
|
||||
border-radius: var(--scrollbar-radius, 999px);
|
||||
}
|
||||
|
||||
.wire-scrollbar::-webkit-scrollbar-thumb {
|
||||
background: color-mix(in srgb, var(--scrollbar-accent) 45%, transparent);
|
||||
border-radius: var(--scrollbar-radius, 999px);
|
||||
}
|
||||
|
||||
.wire-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scrollbar-accent);
|
||||
}
|
||||
|
||||
/*
|
||||
* A pointer-less device paints its own overlay scrollbar and ignores the
|
||||
* width above, so the region keeps its own padding instead.
|
||||
*/
|
||||
@media (hover: none) {
|
||||
.wire-scrollbar {
|
||||
scrollbar-width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
// Image -- a framed image with a reserved aspect ratio.
|
||||
//
|
||||
// <Image src="/hero.jpg" alt="Dashboard" size="video" rounded={true} />
|
||||
//
|
||||
// The frame keeps its ratio whether or not the image has loaded, so the page
|
||||
// does not jump when it arrives. Without a src it renders a labelled
|
||||
// placeholder of the same shape rather than collapsing.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Image {
|
||||
props {
|
||||
size: string = "default"
|
||||
@@ -7,41 +17,111 @@ component Image {
|
||||
width: string = ""
|
||||
height: string = ""
|
||||
loading: string = "lazy"
|
||||
fit: string = "cover"
|
||||
rounded: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
view {
|
||||
<figure
|
||||
{...attrs}
|
||||
data-ui-component="Image"
|
||||
class='relative m-0 overflow-hidden bg-[var(--wire-color-surface-soft)] {class}'
|
||||
class:rounded-2xl='rounded'
|
||||
class='wire-image {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-fit='{fit}'
|
||||
data-rounded='{rounded}'
|
||||
>
|
||||
{#if src}
|
||||
<img
|
||||
src='{src}'
|
||||
alt='{alt}'
|
||||
width='{width}'
|
||||
height='{height}'
|
||||
loading='{loading}'
|
||||
decoding="async"
|
||||
class="block h-auto w-full object-cover transition duration-300"
|
||||
class:aspect-square='size === "square"'
|
||||
class:aspect-video='size === "video"'
|
||||
class:aspect-[4/3]='size === "landscape"'
|
||||
class:aspect-[3/4]='size === "portrait"'
|
||||
/>
|
||||
{:else}
|
||||
<div
|
||||
class="flex min-h-48 w-full items-center justify-center text-[var(--wire-color-text-muted)]"
|
||||
role="img"
|
||||
aria-label='{alt || "Image placeholder"}'
|
||||
>
|
||||
<span class="icon-[lucide--image] size-8" aria-hidden="true"></span>
|
||||
</div>
|
||||
{/if}
|
||||
<img
|
||||
class="wire-image__img"
|
||||
data-show="src"
|
||||
src='{src}'
|
||||
alt='{alt}'
|
||||
width='{width}'
|
||||
height='{height}'
|
||||
loading='{loading}'
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="wire-image__placeholder"
|
||||
data-show="!src"
|
||||
role="img"
|
||||
aria-label='{alt || "Image placeholder"}'
|
||||
>
|
||||
<span class="icon-[lucide--image] wire-image__placeholder-icon" aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
</figure>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-image {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
background: var(--wire-color-surface-soft);
|
||||
}
|
||||
|
||||
.wire-image[data-rounded="true"] {
|
||||
border-radius: var(--wire-radius-lg);
|
||||
}
|
||||
|
||||
/*
|
||||
* The ratio sits on the frame rather than the image, so the space is
|
||||
* reserved before the file arrives and the page does not jump.
|
||||
*/
|
||||
.wire-image[data-size="square"] {
|
||||
aspect-ratio: 1 / 1;
|
||||
}
|
||||
|
||||
.wire-image[data-size="video"] {
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.wire-image[data-size="landscape"] {
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
|
||||
.wire-image[data-size="portrait"] {
|
||||
aspect-ratio: 3 / 4;
|
||||
}
|
||||
|
||||
.wire-image__img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.wire-image[data-fit="contain"] .wire-image__img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.wire-image[data-fit="fill"] .wire-image__img {
|
||||
object-fit: fill;
|
||||
}
|
||||
|
||||
.wire-image[data-size="default"] .wire-image__img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.wire-image__placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
min-height: 12rem;
|
||||
height: 100%;
|
||||
color: var(--wire-color-text-muted);
|
||||
}
|
||||
|
||||
.wire-image__placeholder-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,26 +232,17 @@ component InputNumber {
|
||||
)
|
||||
}
|
||||
|
||||
// Outputs are resolved by name, so each one is written out. Raw
|
||||
// CustomEvents dispatched on the root -- what this did before -- never
|
||||
// reach a parent @binding.
|
||||
client function dispatchInputNumberEvent(
|
||||
sourceEvent,
|
||||
eventName,
|
||||
action,
|
||||
previousValue,
|
||||
root,
|
||||
customEvent
|
||||
payload
|
||||
) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-input-number]")
|
||||
|
||||
if (!root && sourceEvent.target) {
|
||||
root = sourceEvent.target.closest("[data-wrn-input-number]")
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent(eventName, true, false, {
|
||||
payload = {
|
||||
component: "InputNumber",
|
||||
name: name,
|
||||
value: currentValue,
|
||||
@@ -261,8 +252,17 @@ component InputNumber {
|
||||
max: hasMax() ? Number(max) : null,
|
||||
step: normalizedStep(),
|
||||
valid: !isInvalid()
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
}
|
||||
|
||||
if (eventName === "input") {
|
||||
output.input(payload)
|
||||
} else if (eventName === "change") {
|
||||
output.change(payload)
|
||||
} else if (eventName === "increment") {
|
||||
output.increment(payload)
|
||||
} else if (eventName === "decrement") {
|
||||
output.decrement(payload)
|
||||
}
|
||||
}
|
||||
|
||||
client function applyControlValue(nextValue, action, sourceEvent, previousValue) {
|
||||
|
||||
@@ -1,9 +1,55 @@
|
||||
// Kbd -- a keyboard key.
|
||||
//
|
||||
// <Kbd label="Ctrl" />
|
||||
//
|
||||
// Deliberately small. A kbd element is a kbd element; the work here is making
|
||||
// it look like a key and follow the theme.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Kbd {
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "⌘ K"
|
||||
label: string = "K"
|
||||
class: string = ""
|
||||
}
|
||||
view { <kbd class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--kbd {class}"><slot>{label}</slot></kbd> }
|
||||
|
||||
view {
|
||||
<kbd {...attrs} class='wire-kbd {class}' data-size='{size}' data-color='{color}'>
|
||||
<slot>{label}</slot>
|
||||
</kbd>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.6rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
/* The lower edge is what reads as a physical key. */
|
||||
border-bottom-width: 2px;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: var(--wire-color-surface-soft);
|
||||
color: var(--wire-color-text);
|
||||
font-family: inherit;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.wire-kbd[data-size="sm"] {
|
||||
min-width: 1.4rem;
|
||||
padding: 0.1rem 0.3rem;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.wire-kbd[data-size="lg"] {
|
||||
min-width: 2rem;
|
||||
padding: 0.3rem 0.55rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,225 @@
|
||||
// LayoutSplitter -- two panes with a divider the reader can move.
|
||||
//
|
||||
// <LayoutSplitter size={40} minSize={20}>
|
||||
// <div data-slot="start">...</div>
|
||||
// <div data-slot="end">...</div>
|
||||
// </LayoutSplitter>
|
||||
//
|
||||
// The dragging lives in the reactive runtime behind data-wrn-splitter. Pointer
|
||||
// moves fire far too often to route through a client function, and a state
|
||||
// write made inside a pointermove callback is dropped, so the resolved size is
|
||||
// held in the DOM as the --wrn-split custom property and these styles read it.
|
||||
//
|
||||
// This component previously declared resizeStart, resize and resizeEnd with no
|
||||
// pointer handling whatsoever: a caller wired up @resize and received nothing,
|
||||
// for ever, with no error.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component LayoutSplitter {
|
||||
outputs {
|
||||
resizeStart(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
resize(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
resizeEnd(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
// Named sizeChange rather than resize so it cannot be confused with the
|
||||
// native window event a caller may already be listening for. An earlier
|
||||
// comment here claimed a natively-named output could never reach a parent
|
||||
// binding; that was wrong, and the rename was never what fixed anything.
|
||||
sizeChange(payload: { size: number })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
columns: number = 2
|
||||
gap: string = "md"
|
||||
maxWidth: string = "xl"
|
||||
size: number = 50
|
||||
orientation: string = "horizontal"
|
||||
// Smallest share either pane may take, as a percentage. It also fixes the
|
||||
// upper bound at 100 - minSize, so neither pane can be dragged away to
|
||||
// nothing and left unrecoverable by pointer.
|
||||
minSize: number = 15
|
||||
step: number = 5
|
||||
label: string = "Resize panels"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
// The runtime resolves the size and announces it; this turns that into the
|
||||
// declared output so a parent @resize binding receives it.
|
||||
client function reportResize(sourceEvent) {
|
||||
var detail = sourceEvent ? sourceEvent.detail : null
|
||||
if (!detail) {
|
||||
return
|
||||
}
|
||||
output.sizeChange({ size: detail.size })
|
||||
}
|
||||
|
||||
shared function isVertical() {
|
||||
return orientation === "vertical"
|
||||
}
|
||||
|
||||
shared function lowerBound() {
|
||||
var value = Number(minSize)
|
||||
if (!value || value < 0) {
|
||||
return 15
|
||||
}
|
||||
return Math.min(45, value)
|
||||
}
|
||||
|
||||
shared function upperBound() {
|
||||
return 100 - lowerBound()
|
||||
}
|
||||
|
||||
// Sizes arrive as HTML attributes, so a value outside the bounds is
|
||||
// routine rather than exceptional. Clamp instead of rendering something
|
||||
// the reader cannot undo.
|
||||
shared function currentSize() {
|
||||
var value = Number(size)
|
||||
if (!value || value < 0) {
|
||||
return 50
|
||||
}
|
||||
return Math.min(upperBound(), Math.max(lowerBound(), value))
|
||||
}
|
||||
|
||||
shared function stepSize() {
|
||||
var value = Number(step)
|
||||
return value && value > 0 ? value : 5
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="LayoutSplitter"
|
||||
class='wire-splitter {class}'
|
||||
data-color='{color}'
|
||||
data-orientation='{orientation}'
|
||||
data-wrn-splitter='{isVertical() ? "vertical" : "horizontal"}'
|
||||
data-wrn-splitter-min='{lowerBound()}'
|
||||
data-wrn-splitter-step='{stepSize()}'
|
||||
style='--wrn-split:{currentSize()}%;'
|
||||
@wrnexus:splitter:resize='reportResize(event)'
|
||||
>
|
||||
<div class="wire-splitter__pane wire-splitter__pane--start">
|
||||
<slot name="start"></slot>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="wire-splitter__handle"
|
||||
data-wrn-splitter-handle="true"
|
||||
role="separator"
|
||||
tabindex="0"
|
||||
aria-label='{label}'
|
||||
aria-orientation='{isVertical() ? "horizontal" : "vertical"}'
|
||||
aria-valuenow='{currentSize()}'
|
||||
aria-valuemin='{lowerBound()}'
|
||||
aria-valuemax='{upperBound()}'
|
||||
>
|
||||
<span class="wire-splitter__grip" aria-hidden="true"></span>
|
||||
</div>
|
||||
|
||||
<div class="wire-splitter__pane wire-splitter__pane--end">
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-splitter {
|
||||
--splitter-accent: var(--wire-color-primary);
|
||||
display: grid;
|
||||
/* The first track follows the size the runtime resolves. */
|
||||
grid-template-columns: var(--wrn-split, 50%) auto minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-splitter[data-color="secondary"] {
|
||||
--splitter-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-splitter[data-color="success"] {
|
||||
--splitter-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-splitter[data-color="danger"] {
|
||||
--splitter-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-splitter[data-color="info"] {
|
||||
--splitter-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-splitter[data-orientation="vertical"] {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: var(--wrn-split, 50%) auto minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.wire-splitter__pane {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.wire-splitter__handle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
padding: 0 0.25rem;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
cursor: col-resize;
|
||||
/* Without this the pointer drag selects the text in both panes. */
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.wire-splitter[data-orientation="vertical"] .wire-splitter__handle {
|
||||
padding: 0.25rem 0;
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.wire-splitter__grip {
|
||||
display: block;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
min-height: 1.5rem;
|
||||
border-radius: 999px;
|
||||
background: var(--wire-color-border);
|
||||
transition: background 140ms ease;
|
||||
}
|
||||
|
||||
.wire-splitter[data-orientation="vertical"] .wire-splitter__grip {
|
||||
width: 100%;
|
||||
min-width: 1.5rem;
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.wire-splitter__handle:hover .wire-splitter__grip,
|
||||
.wire-splitter[data-wrn-splitter-dragging="true"] .wire-splitter__grip {
|
||||
background: var(--splitter-accent);
|
||||
}
|
||||
|
||||
.wire-splitter__handle:focus-visible {
|
||||
outline: 2px solid var(--splitter-accent);
|
||||
outline-offset: -2px;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Two panes side by side stop making sense on a phone. Stacking them keeps
|
||||
* both readable, and the divider stops being draggable because the grid
|
||||
* no longer has a second track to trade against.
|
||||
*/
|
||||
@media (max-width: 639px) {
|
||||
.wire-splitter,
|
||||
.wire-splitter[data-orientation="vertical"] {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto auto auto;
|
||||
}
|
||||
|
||||
.wire-splitter__handle {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+114
-24
@@ -1,3 +1,13 @@
|
||||
// Link -- a text link that follows the theme.
|
||||
//
|
||||
// <Link href="/docs" label="Documentation" />
|
||||
// <Link href="https://example.com" label="Spec" external={true} />
|
||||
//
|
||||
// external adds rel="noopener noreferrer" and an outbound icon, so the reader
|
||||
// is told the link leaves the site before they follow it.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Link {
|
||||
props {
|
||||
size: string = "default"
|
||||
@@ -6,42 +16,122 @@ component Link {
|
||||
href: string = "#"
|
||||
target: string = ""
|
||||
rel: string = ""
|
||||
underline: string = "hover"
|
||||
external: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
view {
|
||||
<a
|
||||
{...attrs}
|
||||
data-ui-component="Link"
|
||||
class='wire-link {class}'
|
||||
href='{href}'
|
||||
target='{target}'
|
||||
rel='{external ? (rel || "noopener noreferrer") : rel}'
|
||||
class='inline-flex min-w-0 items-center gap-1.5 rounded-md font-semibold underline-offset-4 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--wire-color-background)] {class}'
|
||||
class:text-xs='size === "xs"'
|
||||
class:text-sm='size === "sm" || size === "default"'
|
||||
class:text-base='size === "md"'
|
||||
class:text-lg='size === "lg"'
|
||||
class:text-[var(--wire-color-primary)]='color === "primary"'
|
||||
class:hover:text-[var(--wire-color-primary-hover)]='color === "primary"'
|
||||
class:text-[var(--wire-color-secondary)]='color === "secondary"'
|
||||
class:hover:text-[var(--wire-color-secondary-hover)]='color === "secondary"'
|
||||
class:text-[var(--wire-color-success)]='color === "success"'
|
||||
class:text-[var(--wire-color-warning-text)]='color === "warning"'
|
||||
class:text-[var(--wire-color-danger)]='color === "danger"'
|
||||
class:text-[var(--wire-color-info)]='color === "info"'
|
||||
class:text-[var(--wire-color-text)]='color === "neutral"'
|
||||
class:hover:underline='external === false'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-underline='{underline}'
|
||||
data-external='{external}'
|
||||
>
|
||||
<span class="truncate">{label}</span>
|
||||
|
||||
{#if external}
|
||||
<span
|
||||
class="icon-[lucide--external-link] size-3.5 shrink-0"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{/if}
|
||||
|
||||
<span class="wire-link__label">{label}</span>
|
||||
<span
|
||||
class="icon-[lucide--external-link] wire-link__icon"
|
||||
data-show="external"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<slot></slot>
|
||||
</a>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-link {
|
||||
--link-tone: var(--wire-color-primary);
|
||||
--link-tone-hover: var(--wire-color-primary-hover);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
color: var(--link-tone);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
text-underline-offset: 4px;
|
||||
transition: color 140ms ease;
|
||||
}
|
||||
|
||||
.wire-link[data-color="secondary"] {
|
||||
--link-tone: var(--wire-color-secondary);
|
||||
--link-tone-hover: var(--wire-color-secondary-hover);
|
||||
}
|
||||
|
||||
.wire-link[data-color="success"] {
|
||||
--link-tone: var(--wire-color-success);
|
||||
--link-tone-hover: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-link[data-color="warning"] {
|
||||
--link-tone: var(--wire-color-warning-text);
|
||||
--link-tone-hover: var(--wire-color-warning-text);
|
||||
}
|
||||
|
||||
.wire-link[data-color="danger"] {
|
||||
--link-tone: var(--wire-color-danger);
|
||||
--link-tone-hover: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-link[data-color="info"] {
|
||||
--link-tone: var(--wire-color-info);
|
||||
--link-tone-hover: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-link[data-color="neutral"] {
|
||||
--link-tone: var(--wire-color-text);
|
||||
--link-tone-hover: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-link[data-size="xs"] {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.wire-link[data-size="md"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-link[data-size="lg"] {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.wire-link[data-underline="always"] {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.wire-link[data-underline="hover"]:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.wire-link:hover {
|
||||
color: var(--link-tone-hover);
|
||||
}
|
||||
|
||||
.wire-link:focus-visible {
|
||||
outline: 2px solid var(--wire-color-focus);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* The label truncates; the outbound icon must not shrink with it. */
|
||||
.wire-link__label {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wire-link__icon {
|
||||
flex: 0 0 auto;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
component List {
|
||||
outputs {
|
||||
select(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -52,7 +56,7 @@ component List {
|
||||
class:py-2.5='size === "sm"'
|
||||
class:px-5='size === "lg"'
|
||||
class:py-4='size === "lg"'
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: item, index: index } }))'
|
||||
@click='output.select({ item: item, index: index })'
|
||||
>
|
||||
{#if item.icon}
|
||||
<span class="flex size-10 shrink-0 items-center justify-center rounded-xl bg-[var(--wire-color-primary-soft)] text-[var(--wire-color-primary)]">
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
component Map {
|
||||
outputs {
|
||||
// markerClick and select both fire for a marker press; select is the
|
||||
// generic name callers reach for, markerClick the explicit one.
|
||||
markerClick(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
select(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
zoom(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -54,7 +62,7 @@ component Map {
|
||||
aria-label='{item.label || item.title || "Map marker"}'
|
||||
class="absolute inline-flex size-10 items-center justify-center rounded-full border-4 border-[var(--wire-color-surface-raised)] bg-[var(--wire-color-primary)] text-[var(--wire-color-on-primary)] shadow-lg transition hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
|
||||
style='left: {item.x || (20 + index * 12)}%; top: {item.y || (30 + (index % 3) * 18)}%;'
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("markerClick", { bubbles: true, detail: item })); event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: item }))'
|
||||
@click='output.markerClick(item); output.select(item)'
|
||||
>
|
||||
<span class='{item.icon || "icon-[lucide--map-pin]"}' aria-hidden="true"></span>
|
||||
</button>
|
||||
@@ -66,7 +74,7 @@ component Map {
|
||||
type="button"
|
||||
aria-label="Zoom in"
|
||||
class="inline-flex size-10 items-center justify-center rounded-xl border border-[var(--wire-color-border)] bg-[var(--wire-color-surface-raised)] text-[var(--wire-color-text)] shadow-sm transition hover:bg-[var(--wire-color-surface-soft)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("zoom", { bubbles: true, detail: { direction: "in" } }))'
|
||||
@click='output.zoom({ direction: "in" })'
|
||||
>
|
||||
<span class="icon-[lucide--plus] size-4" aria-hidden="true"></span>
|
||||
</button>
|
||||
@@ -74,7 +82,7 @@ component Map {
|
||||
type="button"
|
||||
aria-label="Zoom out"
|
||||
class="inline-flex size-10 items-center justify-center rounded-xl border border-[var(--wire-color-border)] bg-[var(--wire-color-surface-raised)] text-[var(--wire-color-text)] shadow-sm transition hover:bg-[var(--wire-color-surface-soft)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("zoom", { bubbles: true, detail: { direction: "out" } }))'
|
||||
@click='output.zoom({ direction: "out" })'
|
||||
>
|
||||
<span class="icon-[lucide--minus] size-4" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
component Marquee {
|
||||
outputs {
|
||||
// Fired when the reader pauses the scroll, by hover, focus or the button.
|
||||
pause(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
resume(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -27,8 +33,8 @@ component Marquee {
|
||||
|
||||
<div
|
||||
class="group relative min-w-0 flex-1 overflow-hidden"
|
||||
@mouseenter='paused = true; event.currentTarget.dispatchEvent(new CustomEvent("pause", { bubbles: true }))'
|
||||
@mouseleave='paused = false; event.currentTarget.dispatchEvent(new CustomEvent("resume", { bubbles: true }))'
|
||||
@mouseenter='paused = true; output.pause({})'
|
||||
@mouseleave='paused = false; output.resume({})'
|
||||
@focusin='paused = true'
|
||||
@focusout='paused = false'
|
||||
>
|
||||
@@ -77,7 +83,7 @@ component Marquee {
|
||||
type="button"
|
||||
aria-label='{paused ? "Resume announcements" : "Pause announcements"}'
|
||||
class="flex shrink-0 items-center justify-center border-l border-[var(--wire-color-border)] px-4 text-[var(--wire-color-text-muted)] transition hover:bg-[var(--wire-color-surface-soft)] hover:text-[var(--wire-color-text)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--wire-color-focus)]"
|
||||
@click='paused = !paused; event.currentTarget.dispatchEvent(new CustomEvent(paused ? "pause" : "resume", { bubbles: true }))'
|
||||
@click='paused = !paused; paused ? output.pause({}) : output.resume({})'
|
||||
>
|
||||
<span class="icon-[lucide--pause] size-4" data-show='!paused' aria-hidden="true"></span>
|
||||
<span class="icon-[lucide--play] size-4" data-show='paused' aria-hidden="true"></span>
|
||||
|
||||
@@ -1,23 +1,344 @@
|
||||
// MegaMenu -- a trigger and a wide panel of grouped links.
|
||||
//
|
||||
// <MegaMenu label="Products" columns='[{"heading":"Platform",
|
||||
// "items":[{"label":"Runtime","href":"/runtime"}]}]' />
|
||||
//
|
||||
// Deliberately one level deep. A mega menu exists to show breadth flat, so
|
||||
// everything is one click away; nesting inside the panel buries content behind
|
||||
// hover-within-hover and is close to unusable by keyboard and touch. Use Nav
|
||||
// when you actually want cascading submenus.
|
||||
//
|
||||
// The panel carries data-wrn-anchored so the runtime clamp keeps it inside the
|
||||
// viewport instead of hanging off the edge of a wide layout.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component MegaMenu {
|
||||
outputs {
|
||||
open(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
close(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
open(payload: { sourceEvent: Event })
|
||||
close(payload: { reason: string })
|
||||
select(payload: { item: object; value: string })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Mega Menu"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
size: string = "default"
|
||||
label: string = "Menu"
|
||||
icon: string = ""
|
||||
columns: unknown[] = []
|
||||
footer: string = ""
|
||||
defaultOpen: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state visible = defaultOpen
|
||||
|
||||
functions {
|
||||
shared function columnList() {
|
||||
return Array.isArray(columns) ? columns : []
|
||||
}
|
||||
|
||||
shared function itemsOf(column) {
|
||||
return column && Array.isArray(column.items) ? column.items : []
|
||||
}
|
||||
|
||||
client function showPanel(sourceEvent) {
|
||||
visible = true
|
||||
output.open({ sourceEvent: sourceEvent })
|
||||
}
|
||||
|
||||
client function hidePanel(reason) {
|
||||
visible = false
|
||||
output.close({ reason: reason })
|
||||
}
|
||||
|
||||
client function togglePanel(sourceEvent) {
|
||||
if (visible) {
|
||||
hidePanel("toggle")
|
||||
} else {
|
||||
showPanel(sourceEvent)
|
||||
}
|
||||
}
|
||||
|
||||
client function handleKeydown(sourceEvent) {
|
||||
if (sourceEvent.key === "Escape" && visible) {
|
||||
sourceEvent.preventDefault()
|
||||
hidePanel("escape")
|
||||
}
|
||||
}
|
||||
|
||||
client function choose(item) {
|
||||
if (item.disabled) {
|
||||
return
|
||||
}
|
||||
output.select({ item: item, value: item.value || item.label || "" })
|
||||
hidePanel("select")
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="MegaMenu"
|
||||
class='wire-mega {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-open='{visible}'
|
||||
@keydown='handleKeydown(event)'
|
||||
@mouseleave='hidePanel("pointer-leave")'
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-mega__trigger"
|
||||
aria-haspopup="true"
|
||||
aria-expanded='{visible}'
|
||||
@click='togglePanel(event)'
|
||||
@mouseenter='showPanel(event)'
|
||||
@focus='showPanel(event)'
|
||||
>
|
||||
<span class='wire-mega__trigger-icon {icon}' data-show="icon" aria-hidden="true"></span>
|
||||
<span class="wire-mega__trigger-label">{label}</span>
|
||||
<span class="wire-mega__arrow" aria-hidden="true">›</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
class="wire-mega__panel"
|
||||
data-wrn-anchored="true"
|
||||
data-show="visible"
|
||||
data-wrn-roving="both"
|
||||
>
|
||||
<div class="wire-mega__columns">
|
||||
{#each columnList() as column}
|
||||
<section class="wire-mega__column">
|
||||
<h3 class="wire-mega__heading" data-show="column.heading">{column.heading}</h3>
|
||||
<ul class="wire-mega__list">
|
||||
{#each itemsOf(column) as item}
|
||||
<li>
|
||||
<a
|
||||
class="wire-mega__link"
|
||||
href='{item.href || "#"}'
|
||||
data-wrn-roving-item="true"
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
@click='choose(item)'
|
||||
>
|
||||
<span
|
||||
class='wire-mega__link-icon {item.icon}'
|
||||
data-show="item.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-mega__link-body">
|
||||
<span class="wire-mega__link-label">{item.label}</span>
|
||||
<span class="wire-mega__link-description" data-show="item.description">
|
||||
{item.description}
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<p class="wire-mega__footer" data-show="footer">{footer}</p>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-mega {
|
||||
--mega-accent: var(--wire-color-primary);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-mega[data-color="secondary"] {
|
||||
--mega-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-mega[data-color="success"] {
|
||||
--mega-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-mega[data-color="danger"] {
|
||||
--mega-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-mega[data-color="info"] {
|
||||
--mega-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-mega[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-mega[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-mega__trigger {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: transparent;
|
||||
color: var(--wire-color-text-muted);
|
||||
font: inherit;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-mega__trigger:hover,
|
||||
.wire-mega[data-open="true"] .wire-mega__trigger {
|
||||
background: var(--wire-color-surface-soft);
|
||||
color: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-mega__trigger:focus-visible {
|
||||
outline: 2px solid var(--mega-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-mega__arrow {
|
||||
display: inline-block;
|
||||
transition: transform 160ms ease;
|
||||
}
|
||||
|
||||
.wire-mega[data-open="true"] .wire-mega__arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/*
|
||||
* The panel is offset below the trigger for looks, which used to close the
|
||||
* menu on the way to it: that offset is dead space belonging to neither
|
||||
* element, so crossing it fired mouseleave on the root. The bridge below
|
||||
* covers the gap with a descendant, so the pointer never actually leaves.
|
||||
*/
|
||||
.wire-mega__panel::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
height: 0.6rem;
|
||||
}
|
||||
|
||||
.wire-mega__panel {
|
||||
position: absolute;
|
||||
z-index: 40;
|
||||
top: calc(100% + 0.4rem);
|
||||
left: 0;
|
||||
width: max-content;
|
||||
max-width: min(60rem, calc(100vw - 2rem));
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-lg);
|
||||
background: var(--wire-color-surface);
|
||||
box-shadow: var(--wire-shadow-1);
|
||||
}
|
||||
|
||||
.wire-mega__columns {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
|
||||
gap: 1.25rem;
|
||||
}
|
||||
|
||||
.wire-mega__column {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-mega__heading {
|
||||
margin: 0 0 0.5rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wire-mega__list {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wire-mega__link {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.5rem;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
color: var(--wire-color-text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wire-mega__link:hover {
|
||||
background: var(--wire-color-surface-soft);
|
||||
}
|
||||
|
||||
.wire-mega__link:focus-visible {
|
||||
outline: 2px solid var(--mega-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-mega__link[aria-disabled="true"] {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.wire-mega__link-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-mega__link-label {
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wire-mega__link-description {
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.wire-mega__footer {
|
||||
margin: 1rem 0 0;
|
||||
padding-top: 0.75rem;
|
||||
border-top: 1px solid var(--wire-color-border);
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* On a phone the panel stops floating and becomes part of the flow. A
|
||||
* pointer-opened overlay pinned to a trigger is unreachable on touch, and
|
||||
* a 60rem grid has nowhere to go on a 360px screen.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.wire-mega {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wire-mega__panel {
|
||||
position: static;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.wire-mega__columns {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,354 @@
|
||||
// Nav -- a navigation link list, flat or with submenus.
|
||||
//
|
||||
// <Nav items='[{"label":"Home","href":"/","value":"home"}]' active="home" />
|
||||
//
|
||||
// An item carrying its own items array becomes a submenu. Depth is capped at
|
||||
// three levels: this template language has no component recursion, so each
|
||||
// level is written out, and three covers any realistic navigation.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Nav {
|
||||
outputs {
|
||||
select(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
select(payload: { item: object; value: string })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Nav"
|
||||
size: string = "default"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
label: string = "Main"
|
||||
collapsible: boolean = true
|
||||
toggleLabel: string = "Menu"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state expanded = false
|
||||
|
||||
functions {
|
||||
shared function itemList() {
|
||||
return Array.isArray(items) ? items : []
|
||||
}
|
||||
|
||||
shared function childrenOf(item) {
|
||||
return item && Array.isArray(item.items) ? item.items : []
|
||||
}
|
||||
|
||||
shared function isActive(item) {
|
||||
return Boolean(item.value) && item.value === active
|
||||
}
|
||||
|
||||
shared function rovingAxis() {
|
||||
return orientation === "vertical" ? "vertical" : "horizontal"
|
||||
}
|
||||
|
||||
client function choose(item) {
|
||||
if (item.disabled) {
|
||||
return
|
||||
}
|
||||
output.select({ item: item, value: item.value || "" })
|
||||
}
|
||||
|
||||
client function toggleMenu() {
|
||||
expanded = !expanded
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
<nav
|
||||
{...attrs}
|
||||
data-ui-component="Nav"
|
||||
class='wire-nav {class}'
|
||||
data-orientation='{orientation}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-expanded='{expanded}'
|
||||
role="navigation"
|
||||
aria-label='{label}'
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-nav__toggle"
|
||||
data-show="collapsible"
|
||||
aria-expanded='{expanded}'
|
||||
aria-label='{toggleLabel}'
|
||||
@click='toggleMenu()'
|
||||
>
|
||||
<span class="wire-nav__toggle-bar" aria-hidden="true"></span>
|
||||
<span class="wire-nav__toggle-text">{toggleLabel}</span>
|
||||
</button>
|
||||
|
||||
<ul class="wire-nav__list" data-wrn-roving='{rovingAxis()}'>
|
||||
{#each itemList() as item}
|
||||
<li class="wire-nav__item" data-has-children='{childrenOf(item).length > 0}'>
|
||||
<a
|
||||
class="wire-nav__link"
|
||||
href='{item.href || "#"}'
|
||||
data-wrn-roving-item="true"
|
||||
data-active='{isActive(item)}'
|
||||
aria-current='{isActive(item) ? "page" : "false"}'
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
@click='choose(item)'
|
||||
>
|
||||
<span
|
||||
class='wire-nav__icon {item.icon}'
|
||||
data-show="item.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-nav__label">{item.label}</span>
|
||||
<span class="wire-nav__badge" data-show="item.badge">{item.badge}</span>
|
||||
<span
|
||||
class="wire-nav__arrow"
|
||||
data-show="childrenOf(item).length > 0"
|
||||
aria-hidden="true"
|
||||
>›</span>
|
||||
</a>
|
||||
|
||||
<ul
|
||||
class="wire-nav__submenu"
|
||||
data-wrn-anchored="true"
|
||||
data-show="childrenOf(item).length > 0"
|
||||
>
|
||||
{#each childrenOf(item) as child}
|
||||
<li class="wire-nav__item" data-has-children='{childrenOf(child).length > 0}'>
|
||||
<a
|
||||
class="wire-nav__link"
|
||||
href='{child.href || "#"}'
|
||||
data-active='{isActive(child)}'
|
||||
aria-current='{isActive(child) ? "page" : "false"}'
|
||||
aria-disabled='{child.disabled ? "true" : "false"}'
|
||||
@click='choose(child)'
|
||||
>
|
||||
<span
|
||||
class='wire-nav__icon {child.icon}'
|
||||
data-show="child.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-nav__label">{child.label}</span>
|
||||
<span class="wire-nav__badge" data-show="child.badge">{child.badge}</span>
|
||||
<span
|
||||
class="wire-nav__arrow"
|
||||
data-show="childrenOf(child).length > 0"
|
||||
aria-hidden="true"
|
||||
>›</span>
|
||||
</a>
|
||||
|
||||
<ul
|
||||
class="wire-nav__submenu wire-nav__submenu--level3"
|
||||
data-wrn-anchored="true"
|
||||
data-show="childrenOf(child).length > 0"
|
||||
>
|
||||
{#each childrenOf(child) as leaf}
|
||||
<li class="wire-nav__item">
|
||||
<a
|
||||
class="wire-nav__link"
|
||||
href='{leaf.href || "#"}'
|
||||
data-active='{isActive(leaf)}'
|
||||
aria-current='{isActive(leaf) ? "page" : "false"}'
|
||||
aria-disabled='{leaf.disabled ? "true" : "false"}'
|
||||
@click='choose(leaf)'
|
||||
>
|
||||
<span
|
||||
class='wire-nav__icon {leaf.icon}'
|
||||
data-show="leaf.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-nav__label">{leaf.label}</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<slot />
|
||||
</nav>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-nav {
|
||||
--nav-accent: var(--wire-color-primary);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-nav[data-color="secondary"] {
|
||||
--nav-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-nav[data-color="success"] {
|
||||
--nav-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-nav[data-color="danger"] {
|
||||
--nav-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-nav[data-color="info"] {
|
||||
--nav-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-nav[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-nav[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-nav__list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wire-nav[data-orientation="vertical"] .wire-nav__list {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.wire-nav__item {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wire-nav__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wire-nav__link:hover {
|
||||
background: var(--wire-color-surface-soft);
|
||||
color: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-nav__link:focus-visible {
|
||||
outline: 2px solid var(--nav-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-nav__link[data-active="true"] {
|
||||
background: color-mix(in srgb, var(--nav-accent) 16%, transparent);
|
||||
color: var(--nav-accent);
|
||||
}
|
||||
|
||||
.wire-nav__link[aria-disabled="true"] {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.wire-nav__badge {
|
||||
padding: 0.05rem 0.4rem;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--nav-accent) 16%, transparent);
|
||||
color: var(--nav-accent);
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.wire-nav__arrow {
|
||||
display: inline-block;
|
||||
transition: transform 160ms ease;
|
||||
}
|
||||
|
||||
.wire-nav__item:hover > .wire-nav__link > .wire-nav__arrow,
|
||||
.wire-nav__item:focus-within > .wire-nav__link > .wire-nav__arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.wire-nav__submenu {
|
||||
position: absolute;
|
||||
z-index: 30;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
min-width: 12rem;
|
||||
margin: 0;
|
||||
padding: 0.35rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-surface);
|
||||
box-shadow: var(--wire-shadow-1);
|
||||
list-style: none;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 160ms ease;
|
||||
}
|
||||
|
||||
.wire-nav__item:hover > .wire-nav__submenu,
|
||||
.wire-nav__item:focus-within > .wire-nav__submenu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.wire-nav__submenu--level3 {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.wire-nav__toggle {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: var(--wire-color-surface);
|
||||
color: var(--wire-color-text);
|
||||
font: inherit;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-nav__toggle-bar {
|
||||
width: 1rem;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
box-shadow:
|
||||
0 -5px 0 currentColor,
|
||||
0 5px 0 currentColor;
|
||||
}
|
||||
|
||||
/*
|
||||
* On a phone the bar becomes a disclosure: submenus stop being floating
|
||||
* overlays and stack inline, because a hover-opened overlay is
|
||||
* unreachable on touch.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.wire-nav__toggle {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.wire-nav__list {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.wire-nav[data-expanded="false"] .wire-nav__list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wire-nav__submenu,
|
||||
.wire-nav__submenu--level3 {
|
||||
position: static;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,11 +86,11 @@ size: string = "default"
|
||||
</button>
|
||||
|
||||
<div class="wire-navbar__collapse {mobileOpen ? 'is-open' : ''}">
|
||||
<nav class="wire-navbar__menus" aria-label="{label}">
|
||||
<nav class="wire-navbar__menus" aria-label="{label}" data-wrn-roving="horizontal">
|
||||
{#each items as item}
|
||||
{#if item.children && item.children.length}
|
||||
<details class="wire-navbar__dropdown wire-navbar__dropdown--{item.type || 'dropdown'}" name="wire-navbar-menu" @toggle="toggleDropdown(event, item)">
|
||||
<summary aria-current="{isItemActive(item) ? 'page' : ''}">
|
||||
<summary data-wrn-roving-item="true" aria-current="{isItemActive(item) ? 'page' : 'false'}">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span>{item.label}</span>
|
||||
<span class="wire-navbar__chevron" aria-hidden="true"></span>
|
||||
@@ -103,13 +103,13 @@ size: string = "default"
|
||||
{#if child.label}<strong class="wire-navbar__group-title">{child.label}</strong>{/if}
|
||||
{#if child.description}<small>{child.description}</small>{/if}
|
||||
{#each child.children as nested}
|
||||
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : ''}" @click="selectItem(nested, 3)">
|
||||
<a href="{nested.href || '#'}" target="{nested.target || ''}" rel="{nested.rel || ''}" aria-current="{nested.value === active ? 'page' : 'false'}" @click="selectItem(nested, 3)">
|
||||
{#if nested.icon}<span class="{nested.icon}" aria-hidden="true"></span>{/if}
|
||||
<span><strong>{nested.label}</strong>{#if nested.description}<small>{nested.description}</small>{/if}</span>
|
||||
</a>
|
||||
{/each}
|
||||
{:else}
|
||||
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : ''}" @click="selectItem(child, 2)">
|
||||
<a href="{child.href || '#'}" target="{child.target || ''}" rel="{child.rel || ''}" aria-current="{child.value === active ? 'page' : 'false'}" @click="selectItem(child, 2)">
|
||||
{#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if}
|
||||
<span><strong>{child.label}</strong>{#if child.description}<small>{child.description}</small>{/if}</span>
|
||||
</a>
|
||||
@@ -119,7 +119,7 @@ size: string = "default"
|
||||
</div>
|
||||
</details>
|
||||
{:else}
|
||||
<a class="wire-navbar__menu-link" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : ''}" @click="selectItem(item, 1)">
|
||||
<a class="wire-navbar__menu-link" data-wrn-roving-item="true" href="{item.href || '#'}" target="{item.target || ''}" rel="{item.rel || ''}" aria-current="{item.value === active ? 'page' : 'false'}" @click="selectItem(item, 1)">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span>{item.label}</span>
|
||||
</a>
|
||||
@@ -140,4 +140,440 @@ size: string = "default"
|
||||
</div>
|
||||
</header>
|
||||
}
|
||||
|
||||
style {
|
||||
/* Full application navigation */
|
||||
.wire-navbar {
|
||||
position: relative;
|
||||
z-index: 40;
|
||||
width: 100%;
|
||||
color: var(--wire-color-text);
|
||||
background: color-mix(in srgb, var(--wire-color-surface) 96%, transparent);
|
||||
border-bottom: 1px solid var(--wire-color-border);
|
||||
}
|
||||
|
||||
.wire-navbar--sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
backdrop-filter: blur(16px);
|
||||
}
|
||||
|
||||
.wire-navbar__topbar:empty,
|
||||
.wire-navbar__topbar:not(:has(*)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wire-navbar__topbar:not(:empty) {
|
||||
display: flex;
|
||||
min-height: 2.25rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.35rem clamp(1rem, 4vw, 3rem);
|
||||
color: var(--wire-color-muted);
|
||||
background: var(--wire-color-surface-2);
|
||||
border-bottom: 1px solid var(--wire-color-border);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wire-navbar__main {
|
||||
display: flex;
|
||||
min-height: 4.25rem;
|
||||
width: min(100%, 90rem);
|
||||
margin-inline: auto;
|
||||
padding: 0.75rem clamp(1rem, 4vw, 3rem);
|
||||
align-items: center;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.wire-navbar--width-full .wire-navbar__main {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wire-navbar--width-compact .wire-navbar__main {
|
||||
width: min(86%, 80rem);
|
||||
}
|
||||
|
||||
.wire-navbar__brand,
|
||||
.wire-navbar__menu-link,
|
||||
.wire-navbar__dropdown summary,
|
||||
.wire-navbar__action,
|
||||
.wire-navbar__panel a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wire-navbar__brand {
|
||||
display: inline-flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.wire-navbar__brand-logo {
|
||||
display: block;
|
||||
width: auto;
|
||||
max-width: 11rem;
|
||||
height: 2.5rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.wire-navbar__brand-icon {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
color: var(--wire-component-color, var(--wire-color-primary));
|
||||
}
|
||||
|
||||
.wire-navbar__brand-copy {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.wire-navbar__brand-copy strong {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wire-navbar__brand-copy small {
|
||||
margin-top: 0.2rem;
|
||||
color: var(--wire-color-muted);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wire-navbar__collapse,
|
||||
.wire-navbar__menus,
|
||||
.wire-navbar__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wire-navbar__collapse {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.wire-navbar__menus {
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.wire-navbar__actions {
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.wire-navbar__menu-link,
|
||||
.wire-navbar__dropdown > summary,
|
||||
.wire-navbar__action {
|
||||
display: inline-flex;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.65rem 0.8rem;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
border-radius: 0.65rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wire-navbar__menu-link:hover,
|
||||
.wire-navbar__dropdown > summary:hover {
|
||||
color: var(--wire-color-text);
|
||||
background: var(--wire-color-surface-2);
|
||||
}
|
||||
|
||||
.wire-navbar__menu-link[aria-current="page"],
|
||||
.wire-navbar__dropdown > summary[aria-current="page"] {
|
||||
color: var(--wire-component-color, var(--wire-color-primary));
|
||||
font-weight: 600;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--wire-component-color, var(--wire-color-primary)) 12%,
|
||||
transparent
|
||||
);
|
||||
box-shadow: inset 0 -2px 0 var(--wire-component-color, var(--wire-color-primary));
|
||||
}
|
||||
|
||||
.wire-navbar__menu-link[aria-current="page"]:hover,
|
||||
.wire-navbar__dropdown > summary[aria-current="page"]:hover {
|
||||
color: var(--wire-component-color, var(--wire-color-primary));
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
var(--wire-component-color, var(--wire-color-primary)) 16%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown > summary {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wire-navbar__chevron {
|
||||
width: 0.45rem;
|
||||
height: 0.45rem;
|
||||
border-right: 1.5px solid currentColor;
|
||||
border-bottom: 1.5px solid currentColor;
|
||||
transform: rotate(45deg) translateY(-0.15rem);
|
||||
transition: transform var(--wire-motion-fast, 150ms) ease;
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown[open] .wire-navbar__chevron {
|
||||
transform: rotate(225deg) translate(-0.1rem, -0.1rem);
|
||||
}
|
||||
|
||||
.wire-navbar__panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.55rem);
|
||||
left: 50%;
|
||||
display: grid;
|
||||
width: max-content;
|
||||
min-width: 15rem;
|
||||
max-width: min(90vw, 64rem);
|
||||
padding: 0.65rem;
|
||||
gap: 0.45rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: 0.9rem;
|
||||
color: var(--wire-color-text);
|
||||
background: var(--wire-color-surface);
|
||||
box-shadow: 0 18px 48px rgb(15 23 42 / 0.16);
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0) scale(1);
|
||||
transform-origin: top center;
|
||||
transition:
|
||||
opacity 180ms ease,
|
||||
transform 180ms cubic-bezier(0.22, 1, 0.36, 1),
|
||||
display 180ms allow-discrete;
|
||||
}
|
||||
|
||||
.wire-navbar__panel::before {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
height: 0.65rem;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown:not([open]) > .wire-navbar__panel {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(-0.5rem) scale(0.98);
|
||||
}
|
||||
|
||||
@starting-style {
|
||||
.wire-navbar__dropdown[open] > .wire-navbar__panel {
|
||||
opacity: 0;
|
||||
transform: translateX(-50%) translateY(-0.5rem) scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown--mega {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.wire-navbar__dropdown--mega .wire-navbar__panel {
|
||||
right: auto;
|
||||
left: 50%;
|
||||
width: min(calc(100vw - 2rem), 64rem);
|
||||
}
|
||||
|
||||
.wire-navbar__panel--columns-2 {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-navbar__panel--columns-3 {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-navbar__panel--columns-4 {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-navbar__panel-intro {
|
||||
grid-column: 1 / -1;
|
||||
margin: 0;
|
||||
padding: 0.5rem 0.65rem;
|
||||
color: var(--wire-color-muted);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wire-navbar__group {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.wire-navbar__group-title {
|
||||
padding: 0.55rem 0.65rem 0.25rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wire-navbar__group > small {
|
||||
padding: 0 0.65rem 0.4rem;
|
||||
color: var(--wire-color-muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wire-navbar__panel a {
|
||||
display: flex;
|
||||
min-height: 2.75rem;
|
||||
padding: 0.65rem;
|
||||
align-items: flex-start;
|
||||
gap: 0.65rem;
|
||||
border-radius: 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 450;
|
||||
}
|
||||
|
||||
.wire-navbar__panel a:hover {
|
||||
background: var(--wire-color-surface-2);
|
||||
}
|
||||
|
||||
.wire-navbar__panel a span:last-child {
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.wire-navbar__panel a strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wire-navbar__panel a small {
|
||||
color: var(--wire-color-muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.wire-navbar__action {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.wire-navbar__action--primary {
|
||||
color: var(--wire-color-on-primary, #fff);
|
||||
background: var(--wire-component-color, var(--wire-color-primary));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wire-navbar__action--outline {
|
||||
border-color: var(--wire-color-border);
|
||||
}
|
||||
|
||||
.wire-navbar__toggle {
|
||||
display: none;
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
margin-left: auto;
|
||||
padding: 0.65rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: 0.65rem;
|
||||
color: inherit;
|
||||
background: var(--wire-color-surface);
|
||||
}
|
||||
|
||||
.wire-navbar__toggle span {
|
||||
display: block;
|
||||
height: 2px;
|
||||
margin: 0.25rem 0;
|
||||
background: currentColor;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.wire-navbar--width-compact .wire-navbar__main {
|
||||
width: 100%;
|
||||
}
|
||||
.wire-navbar__toggle {
|
||||
display: block;
|
||||
}
|
||||
.wire-navbar__collapse {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
padding: 0.75rem 1rem 1rem;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
background: var(--wire-color-surface);
|
||||
border-bottom: 1px solid var(--wire-color-border);
|
||||
box-shadow: 0 16px 32px rgb(15 23 42 / 0.12);
|
||||
max-height: calc(100dvh - 4rem);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
.wire-navbar__collapse.is-open {
|
||||
display: flex;
|
||||
}
|
||||
.wire-navbar__menus,
|
||||
.wire-navbar__actions {
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
.wire-navbar__menu-link,
|
||||
.wire-navbar__dropdown > summary,
|
||||
.wire-navbar__action {
|
||||
width: 100%;
|
||||
}
|
||||
.wire-navbar__panel,
|
||||
.wire-navbar__dropdown--mega .wire-navbar__panel {
|
||||
position: static;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin-top: 0.25rem;
|
||||
box-shadow: none;
|
||||
transform: none;
|
||||
overflow: hidden;
|
||||
animation: wire-navbar-mobile-panel 180ms ease both;
|
||||
}
|
||||
.wire-navbar__dropdown:not([open]) > .wire-navbar__panel,
|
||||
.wire-navbar__dropdown[open] > .wire-navbar__panel {
|
||||
transform: none;
|
||||
}
|
||||
.wire-navbar__panel[class*="wire-navbar__panel--columns-"] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.wire-navbar__panel::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.wire-navbar__brand-copy small {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wire-navbar-mobile-panel {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-0.35rem);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,291 @@
|
||||
// Pagination -- page controls over a known total.
|
||||
//
|
||||
// <Pagination page={2} pageSize={10} total={137} variant="numbered" />
|
||||
//
|
||||
// The component owns no data. It reports the requested page through its
|
||||
// change output and lets the caller fetch or slice.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Pagination {
|
||||
outputs {
|
||||
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
previous(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
next(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
change(payload: { page: number; pageSize: number })
|
||||
previous(payload: { page: number })
|
||||
next(payload: { page: number })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
size: string = "default"
|
||||
page: number = 1
|
||||
pageSize: number = 10
|
||||
total: number = 0
|
||||
variant: string = "compact"
|
||||
siblingCount: number = 1
|
||||
showSummary: boolean = true
|
||||
label: string = "Pagination"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
previousLabel: string = "Previous"
|
||||
nextLabel: string = "Next"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function lastPage() {
|
||||
var size = Number(pageSize) > 0 ? Number(pageSize) : 10
|
||||
var count = Number(total) > 0 ? Number(total) : 0
|
||||
return Math.max(1, Math.ceil(count / size))
|
||||
}
|
||||
|
||||
// Out-of-range values arrive routinely: page travels as an HTML attribute
|
||||
// and callers compute it from data that may have shrunk underneath them.
|
||||
shared function currentPage() {
|
||||
var value = Number(page)
|
||||
if (!value || value < 1) {
|
||||
return 1
|
||||
}
|
||||
return Math.min(value, lastPage())
|
||||
}
|
||||
|
||||
shared function firstShown() {
|
||||
if (Number(total) < 1) {
|
||||
return 0
|
||||
}
|
||||
return (currentPage() - 1) * (Number(pageSize) || 10) + 1
|
||||
}
|
||||
|
||||
shared function lastShown() {
|
||||
return Math.min(currentPage() * (Number(pageSize) || 10), Number(total) || 0)
|
||||
}
|
||||
|
||||
shared function pageNumbers() {
|
||||
var last = lastPage()
|
||||
var current = currentPage()
|
||||
var siblings = Math.max(0, Number(siblingCount) || 0)
|
||||
var start = Math.max(1, current - siblings)
|
||||
var end = Math.min(last, current + siblings)
|
||||
var pages = []
|
||||
if (start > 1) {
|
||||
pages.push({ value: 1, label: "1", gap: false })
|
||||
if (start > 2) {
|
||||
pages.push({ value: 0, label: "...", gap: true })
|
||||
}
|
||||
}
|
||||
for (var index = start; index <= end; index += 1) {
|
||||
pages.push({ value: index, label: String(index), gap: false })
|
||||
}
|
||||
if (end < last) {
|
||||
if (end < last - 1) {
|
||||
pages.push({ value: 0, label: "...", gap: true })
|
||||
}
|
||||
pages.push({ value: last, label: String(last), gap: false })
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
client function goToPage(target) {
|
||||
var next = Math.min(Math.max(1, Number(target) || 1), lastPage())
|
||||
output.change({ page: next, pageSize: Number(pageSize) || 10 })
|
||||
}
|
||||
|
||||
client function goPrevious() {
|
||||
var target = Math.max(1, currentPage() - 1)
|
||||
output.previous({ page: target })
|
||||
output.change({ page: target, pageSize: Number(pageSize) || 10 })
|
||||
}
|
||||
|
||||
client function goNext() {
|
||||
var target = Math.min(lastPage(), currentPage() + 1)
|
||||
output.next({ page: target })
|
||||
output.change({ page: target, pageSize: Number(pageSize) || 10 })
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
<nav
|
||||
{...attrs}
|
||||
data-ui-component="Pagination"
|
||||
class='wire-pagination {class}'
|
||||
data-variant='{variant}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
role="navigation"
|
||||
aria-label='{label}'
|
||||
>
|
||||
<p class="wire-pagination__summary" data-show="showSummary">
|
||||
{firstShown()} to {lastShown()} of {total}
|
||||
</p>
|
||||
|
||||
<div class="wire-pagination__controls">
|
||||
<button
|
||||
type="button"
|
||||
class="wire-pagination__step"
|
||||
aria-label='{previousLabel}'
|
||||
@click='goPrevious()'
|
||||
>
|
||||
<span class="wire-pagination__step-icon" aria-hidden="true">‹</span>
|
||||
<span class="wire-pagination__step-label">{previousLabel}</span>
|
||||
</button>
|
||||
|
||||
<ol class="wire-pagination__pages" data-show="variant === 'numbered'">
|
||||
{#each pageNumbers() as entry}
|
||||
<li class="wire-pagination__slot">
|
||||
<span class="wire-pagination__gap" data-show="entry.gap">{entry.label}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-pagination__page"
|
||||
data-show="!entry.gap"
|
||||
data-active='{entry.value === currentPage()}'
|
||||
aria-current='{entry.value === currentPage() ? "page" : "false"}'
|
||||
@click='goToPage(entry.value)'
|
||||
>
|
||||
{entry.label}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
|
||||
<p class="wire-pagination__compact" data-show="variant !== 'numbered'">
|
||||
{currentPage()} / {lastPage()}
|
||||
</p>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="wire-pagination__step"
|
||||
aria-label='{nextLabel}'
|
||||
@click='goNext()'
|
||||
>
|
||||
<span class="wire-pagination__step-label">{nextLabel}</span>
|
||||
<span class="wire-pagination__step-icon" aria-hidden="true">›</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</nav>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-pagination {
|
||||
--pagination-accent: var(--wire-color-primary);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-pagination[data-color="secondary"] {
|
||||
--pagination-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-pagination[data-color="success"] {
|
||||
--pagination-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-pagination[data-color="danger"] {
|
||||
--pagination-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-pagination[data-color="info"] {
|
||||
--pagination-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-pagination[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-pagination[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-pagination__summary {
|
||||
margin: 0;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-pagination__controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.wire-pagination__pages {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wire-pagination__slot {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.wire-pagination__page,
|
||||
.wire-pagination__step {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
min-width: 2.25rem;
|
||||
justify-content: center;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: var(--wire-color-surface);
|
||||
color: var(--wire-color-text);
|
||||
font: inherit;
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-pagination__page:hover,
|
||||
.wire-pagination__step:hover {
|
||||
background: var(--wire-color-surface-soft);
|
||||
}
|
||||
|
||||
.wire-pagination__page:focus-visible,
|
||||
.wire-pagination__step:focus-visible {
|
||||
outline: 2px solid var(--pagination-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-pagination__page[data-active="true"] {
|
||||
border-color: var(--pagination-accent);
|
||||
background: var(--pagination-accent);
|
||||
color: var(--wire-color-primary-contrast);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wire-pagination__gap {
|
||||
padding: 0 0.35rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
}
|
||||
|
||||
.wire-pagination__compact {
|
||||
margin: 0;
|
||||
padding: 0 0.5rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Below the small breakpoint the word labels crowd the arrows out. */
|
||||
@media (max-width: 639px) {
|
||||
.wire-pagination {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.wire-pagination__step-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wire-pagination__summary {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,172 @@
|
||||
// Scrollspy -- a table of contents that follows the reader.
|
||||
//
|
||||
// <Scrollspy items='[{"label":"Overview","href":"#overview"}]' />
|
||||
//
|
||||
// Each href points at an element on the page. The runtime observes those
|
||||
// elements and moves aria-current to the link for whichever one is in view.
|
||||
//
|
||||
// The runtime writes the marker straight onto the links rather than into
|
||||
// component state. An IntersectionObserver callback fires long after the
|
||||
// client function that registered it has returned, and a state write made
|
||||
// there is dropped -- so the DOM is the only place the answer can live.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Scrollspy {
|
||||
outputs {
|
||||
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
change(payload: { href: string; label: string })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Scrollspy"
|
||||
size: string = "default"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
label: string = "On this page"
|
||||
heading: string = ""
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function itemList() {
|
||||
return Array.isArray(items) ? items : []
|
||||
}
|
||||
|
||||
shared function isActive(item) {
|
||||
return Boolean(item.href) && item.href === active
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
<nav
|
||||
{...attrs}
|
||||
data-ui-component="Scrollspy"
|
||||
class='wire-scrollspy {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-wrn-scrollspy="true"
|
||||
role="navigation"
|
||||
aria-label='{label}'
|
||||
>
|
||||
<p class="wire-scrollspy__heading" data-show="heading">{heading}</p>
|
||||
|
||||
<ul class="wire-scrollspy__list">
|
||||
{#each itemList() as item}
|
||||
<li class="wire-scrollspy__item">
|
||||
<a
|
||||
class="wire-scrollspy__link"
|
||||
href='{item.href || "#"}'
|
||||
data-active='{isActive(item)}'
|
||||
aria-current='{isActive(item) ? "location" : "false"}'
|
||||
>
|
||||
<span class="wire-scrollspy__label">{item.label}</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<slot />
|
||||
</nav>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-scrollspy {
|
||||
--scrollspy-accent: var(--wire-color-primary);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-color="secondary"] {
|
||||
--scrollspy-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-color="success"] {
|
||||
--scrollspy-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-color="danger"] {
|
||||
--scrollspy-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-color="info"] {
|
||||
--scrollspy-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-scrollspy[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-scrollspy__heading {
|
||||
margin: 0 0 0.5rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wire-scrollspy__list {
|
||||
display: grid;
|
||||
gap: 0.1rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
border-left: 1px solid var(--wire-color-border);
|
||||
}
|
||||
|
||||
.wire-scrollspy__link {
|
||||
display: block;
|
||||
padding: 0.3rem 0.75rem;
|
||||
margin-left: -1px;
|
||||
border-left: 2px solid transparent;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.85rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wire-scrollspy__link:hover {
|
||||
color: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-scrollspy__link:focus-visible {
|
||||
outline: 2px solid var(--scrollspy-accent);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.wire-scrollspy__link[data-active="true"] {
|
||||
border-left-color: var(--scrollspy-accent);
|
||||
color: var(--scrollspy-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/*
|
||||
* A table of contents is a sidebar affordance. On a phone it stops being
|
||||
* a rail and becomes a horizontal strip that scrolls, so it costs one
|
||||
* line rather than a screenful.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.wire-scrollspy__list {
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: max-content;
|
||||
overflow-x: auto;
|
||||
border-left: 0;
|
||||
border-bottom: 1px solid var(--wire-color-border);
|
||||
}
|
||||
|
||||
.wire-scrollspy__link {
|
||||
margin-left: 0;
|
||||
border-left: 0;
|
||||
border-bottom: 2px solid transparent;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.wire-scrollspy__link[data-active="true"] {
|
||||
border-left-color: transparent;
|
||||
border-bottom-color: var(--scrollspy-accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
component SearchBox {
|
||||
outputs {
|
||||
search(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
clear(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -22,7 +27,7 @@ component SearchBox {
|
||||
data-ui-component="SearchBox"
|
||||
role="search"
|
||||
class='w-full {class}'
|
||||
@submit='event.preventDefault(); event.currentTarget.dispatchEvent(new CustomEvent("search", { bubbles: true, detail: { value: query, name: name } }))'
|
||||
@submit='event.preventDefault(); output.search({ value: query, name: name })'
|
||||
>
|
||||
<label
|
||||
class="mb-2 block text-sm font-semibold text-[var(--wire-color-text)]"
|
||||
@@ -61,7 +66,7 @@ component SearchBox {
|
||||
aria-label="Clear search"
|
||||
data-show='query.length > 0 && !disabled'
|
||||
class="absolute right-12 inline-flex size-8 items-center justify-center rounded-lg text-[var(--wire-color-text-muted)] transition hover:bg-[var(--wire-color-surface-soft)] hover:text-[var(--wire-color-text)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
|
||||
@click='query = ""; event.currentTarget.parentElement.querySelector("input")?.focus(); event.currentTarget.dispatchEvent(new CustomEvent("clear", { bubbles: true, detail: { value: "", name: name } }))'
|
||||
@click='query = ""; event.currentTarget.parentElement.querySelector("input")?.focus(); output.clear({ value: "", name: name })'
|
||||
>
|
||||
<span class="icon-[lucide--x] size-4" aria-hidden="true"></span>
|
||||
</button>
|
||||
|
||||
@@ -1,82 +1,335 @@
|
||||
// Sidebar -- a vertical navigation rail that becomes a drawer on small screens.
|
||||
//
|
||||
// <Sidebar items='[{"label":"Dashboard","href":"/","value":"dash"}]' active="dash" />
|
||||
//
|
||||
// An entry is one of three shapes:
|
||||
// { label, href, value } a single link
|
||||
// { heading, items: [...] } a labelled group
|
||||
// { label, items: [...] } a collapsible branch, nested to 3 levels
|
||||
//
|
||||
// The off-canvas presentation is Drawer rather than a second implementation,
|
||||
// so the focus trap and the body scroll lock come from one place.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
import Drawer from "./Drawer.wrn"
|
||||
|
||||
component Sidebar {
|
||||
outputs {
|
||||
toggle(payload: { open: boolean })
|
||||
open(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
open(payload: { sourceEvent: Event })
|
||||
close(payload: { source: string })
|
||||
select(payload: { item: string | number | boolean | null | object; value: string | number | boolean; level: string })
|
||||
select(payload: { item: object; value: string; level: number })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
size: string = "default"
|
||||
label: string = "Sidebar"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
mobileLabel: string = "Open navigation"
|
||||
drawerTitle: string = "Navigation"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state mobileOpen: boolean = false
|
||||
state drawerOpen = false
|
||||
|
||||
functions {
|
||||
client function toggleSidebar() {
|
||||
mobileOpen = !mobileOpen
|
||||
output.toggle({ open: mobileOpen })
|
||||
output[mobileOpen ? "open" : "close"]({ source: "mobile" })
|
||||
shared function entryList() {
|
||||
return Array.isArray(items) ? items : []
|
||||
}
|
||||
|
||||
client function closeSidebar() {
|
||||
mobileOpen = false
|
||||
output.close({ source: "mobile" })
|
||||
// Accepts children as well as items: children is what shipped in 0.8.5
|
||||
// and existing callers should keep working.
|
||||
shared function childrenOf(entry) {
|
||||
if (!entry) {
|
||||
return []
|
||||
}
|
||||
if (Array.isArray(entry.items)) {
|
||||
return entry.items
|
||||
}
|
||||
return Array.isArray(entry.children) ? entry.children : []
|
||||
}
|
||||
|
||||
client function selectItem(item, level) {
|
||||
mobileOpen = false
|
||||
output.select({ item: item, value: item.value || "", level: level })
|
||||
shared function isGroup(entry) {
|
||||
return Boolean(entry.heading)
|
||||
}
|
||||
|
||||
shared function isActive(entry) {
|
||||
return Boolean(entry.value) && entry.value === active
|
||||
}
|
||||
|
||||
client function choose(entry, level) {
|
||||
if (entry.disabled) {
|
||||
return
|
||||
}
|
||||
output.select({ item: entry, value: entry.value || "", level: level })
|
||||
if (drawerOpen) {
|
||||
drawerOpen = false
|
||||
output.close({ source: "select" })
|
||||
}
|
||||
}
|
||||
|
||||
client function openDrawer(sourceEvent) {
|
||||
drawerOpen = true
|
||||
output.open({ sourceEvent: sourceEvent })
|
||||
output.toggle({ open: true })
|
||||
}
|
||||
|
||||
client function closeDrawer() {
|
||||
drawerOpen = false
|
||||
output.close({ source: "drawer" })
|
||||
output.toggle({ open: false })
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<div class="wire-sidebar-shell wire-next--color-{color} wire-next--size-{size} {mobileOpen ? 'is-open' : ''} {class}">
|
||||
<button type="button" class="wire-sidebar-toggle" aria-label="{mobileLabel}" aria-expanded="{mobileOpen}" @click="toggleSidebar()">
|
||||
<span class="icon-[lucide--panel-left-open]" aria-hidden="true"></span>
|
||||
<span>{label}</span>
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Sidebar"
|
||||
class='wire-sidebar {class}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-sidebar__launcher"
|
||||
aria-label='{mobileLabel}'
|
||||
aria-expanded='{drawerOpen}'
|
||||
@click='openDrawer(event)'
|
||||
>
|
||||
<span class="wire-sidebar__launcher-bar" aria-hidden="true"></span>
|
||||
<span class="wire-sidebar__launcher-text">{mobileLabel}</span>
|
||||
</button>
|
||||
<button type="button" class="wire-sidebar-backdrop" aria-label="Close navigation" @click="closeSidebar()"></button>
|
||||
<nav class="wire-next wire-next--sidebar wire-next--{orientation} wire-sidebar-panel" aria-label="{label}">
|
||||
<div class="wire-sidebar-panel__head">
|
||||
<strong>{label}</strong>
|
||||
<button type="button" aria-label="Close navigation" @click="closeSidebar()"><span class="icon-[lucide--x]" aria-hidden="true"></span></button>
|
||||
</div>
|
||||
<div class="wire-sidebar-items">
|
||||
{#each items as item}
|
||||
{#if item.children && item.children.length}
|
||||
<details class="wire-sidebar-group" name="wire-sidebar-group">
|
||||
<summary>
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span>{item.label}</span>
|
||||
<span class="icon-[lucide--chevron-down]" aria-hidden="true"></span>
|
||||
</summary>
|
||||
<div class="wire-sidebar-group__children">
|
||||
{#each item.children as child}
|
||||
<a href="{child.href || '#'}" aria-current="{child.value === active ? 'page' : ''}" @click="selectItem(child, 2)">
|
||||
{#if child.icon}<span class="{child.icon}" aria-hidden="true"></span>{/if}
|
||||
<span>{child.label}</span>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</details>
|
||||
{:else}
|
||||
<a href="{item.href || '#'}" aria-current="{item.value === active ? 'page' : ''}" @click="selectItem(item, 1)">
|
||||
{#if item.icon}<span class="{item.icon}" aria-hidden="true"></span>{/if}
|
||||
<span>{item.label}</span>
|
||||
|
||||
<nav class="wire-sidebar__rail" aria-label='{label}'>
|
||||
<ul class="wire-sidebar__list" data-wrn-roving="vertical">
|
||||
{#each entryList() as entry}
|
||||
<li class="wire-sidebar__entry" data-group='{isGroup(entry)}'>
|
||||
<p class="wire-sidebar__heading" data-show="entry.heading">{entry.heading}</p>
|
||||
|
||||
<a
|
||||
class="wire-sidebar__link"
|
||||
data-show="!entry.heading"
|
||||
href='{entry.href || "#"}'
|
||||
data-wrn-roving-item="true"
|
||||
data-active='{isActive(entry)}'
|
||||
aria-current='{isActive(entry) ? "page" : "false"}'
|
||||
aria-disabled='{entry.disabled ? "true" : "false"}'
|
||||
@click='choose(entry, 1)'
|
||||
>
|
||||
<span
|
||||
class='wire-sidebar__icon {entry.icon}'
|
||||
data-show="entry.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-sidebar__label">{entry.label}</span>
|
||||
<span class="wire-sidebar__badge" data-show="entry.badge">{entry.badge}</span>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<ul class="wire-sidebar__sublist" data-show="childrenOf(entry).length > 0">
|
||||
{#each childrenOf(entry) as child}
|
||||
<li class="wire-sidebar__entry">
|
||||
<a
|
||||
class="wire-sidebar__link"
|
||||
href='{child.href || "#"}'
|
||||
data-active='{isActive(child)}'
|
||||
aria-current='{isActive(child) ? "page" : "false"}'
|
||||
aria-disabled='{child.disabled ? "true" : "false"}'
|
||||
@click='choose(child, 2)'
|
||||
>
|
||||
<span
|
||||
class='wire-sidebar__icon {child.icon}'
|
||||
data-show="child.icon"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
<span class="wire-sidebar__label">{child.label}</span>
|
||||
<span class="wire-sidebar__badge" data-show="child.badge">{child.badge}</span>
|
||||
</a>
|
||||
|
||||
<ul
|
||||
class="wire-sidebar__sublist wire-sidebar__sublist--level3"
|
||||
data-show="childrenOf(child).length > 0"
|
||||
>
|
||||
{#each childrenOf(child) as leaf}
|
||||
<li class="wire-sidebar__entry">
|
||||
<a
|
||||
class="wire-sidebar__link"
|
||||
href='{leaf.href || "#"}'
|
||||
data-active='{isActive(leaf)}'
|
||||
aria-current='{isActive(leaf) ? "page" : "false"}'
|
||||
@click='choose(leaf, 3)'
|
||||
>
|
||||
<span class="wire-sidebar__label">{leaf.label}</span>
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</li>
|
||||
{/each}
|
||||
</div>
|
||||
</ul>
|
||||
<slot />
|
||||
</nav>
|
||||
|
||||
<Drawer
|
||||
open={drawerOpen}
|
||||
placement="left"
|
||||
title={drawerTitle}
|
||||
label={label}
|
||||
@close="closeDrawer()"
|
||||
>
|
||||
<slot name="drawer"></slot>
|
||||
</Drawer>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-sidebar {
|
||||
--sidebar-accent: var(--wire-color-primary);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-sidebar[data-color="secondary"] {
|
||||
--sidebar-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-sidebar[data-color="success"] {
|
||||
--sidebar-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-sidebar[data-color="danger"] {
|
||||
--sidebar-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-sidebar[data-color="info"] {
|
||||
--sidebar-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-sidebar[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-sidebar[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-sidebar__rail {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wire-sidebar__list,
|
||||
.wire-sidebar__sublist {
|
||||
display: grid;
|
||||
gap: 0.1rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.wire-sidebar__sublist {
|
||||
margin-left: 0.85rem;
|
||||
padding-left: 0.5rem;
|
||||
border-left: 1px solid var(--wire-color-border);
|
||||
}
|
||||
|
||||
.wire-sidebar__entry[data-group="true"] + .wire-sidebar__entry {
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.wire-sidebar__heading {
|
||||
margin: 0.9rem 0 0.35rem;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wire-sidebar__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.6rem;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wire-sidebar__link:hover {
|
||||
background: var(--wire-color-surface-soft);
|
||||
color: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-sidebar__link:focus-visible {
|
||||
outline: 2px solid var(--sidebar-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-sidebar__link[data-active="true"] {
|
||||
background: color-mix(in srgb, var(--sidebar-accent) 16%, transparent);
|
||||
color: var(--sidebar-accent);
|
||||
}
|
||||
|
||||
.wire-sidebar__link[aria-disabled="true"] {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.wire-sidebar__label {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-sidebar__badge {
|
||||
margin-left: auto;
|
||||
padding: 0.05rem 0.4rem;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--sidebar-accent) 16%, transparent);
|
||||
color: var(--sidebar-accent);
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.wire-sidebar__launcher {
|
||||
display: none;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: var(--wire-color-surface);
|
||||
color: var(--wire-color-text);
|
||||
font: inherit;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-sidebar__launcher-bar {
|
||||
width: 1rem;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
box-shadow:
|
||||
0 -5px 0 currentColor,
|
||||
0 5px 0 currentColor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below the tablet breakpoint the rail is replaced by the drawer launcher.
|
||||
* A permanent rail eats most of a phone screen, and Drawer already handles
|
||||
* the focus trap and the scroll lock.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
.wire-sidebar__launcher {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.wire-sidebar__rail {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,442 @@
|
||||
// Stepper -- ordered progress through a sequence.
|
||||
//
|
||||
// <Stepper steps='[{"label":"Account"},{"label":"Billing"}]' active={1} />
|
||||
//
|
||||
// Each step can be authored by hand instead of using the built-in body, by
|
||||
// passing a slot named for its index:
|
||||
//
|
||||
// <Stepper steps={steps} active={1}>
|
||||
// <div data-slot="step-1">...anything...</div>
|
||||
// </Stepper>
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Stepper {
|
||||
outputs {
|
||||
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
|
||||
previous(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
next(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
complete(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
change(payload: { index: number; step: object })
|
||||
back(payload: { index: number; step: object })
|
||||
next(payload: { index: number; step: object })
|
||||
skip(payload: { index: number; step: object })
|
||||
finish(payload: { index: number; step: object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Stepper"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
size: string = "default"
|
||||
steps: unknown[] = []
|
||||
active: number = 0
|
||||
orientation: string = "horizontal"
|
||||
clickable: boolean = false
|
||||
label: string = "Progress"
|
||||
// Render each step body, showing only the active one -- same contract as
|
||||
// Tabs, so a wizard does not have to hand-roll the panel switching.
|
||||
showPanel: boolean = false
|
||||
controls: boolean = false
|
||||
allowSkip: boolean = false
|
||||
// Lets a form hold the step. The component never validates anything
|
||||
// itself: the page owns the form, so it sets this while the step is
|
||||
// incomplete and clears it once the step passes.
|
||||
nextDisabled: boolean = false
|
||||
backLabel: string = "Back"
|
||||
nextLabel: string = "Next"
|
||||
skipLabel: string = "Skip"
|
||||
finishLabel: string = "Finish"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function stepList() {
|
||||
return Array.isArray(steps) ? steps : []
|
||||
}
|
||||
|
||||
shared function activeIndex() {
|
||||
var count = stepList().length
|
||||
if (count < 1) {
|
||||
return 0
|
||||
}
|
||||
var value = Number(active)
|
||||
if (!value || value < 0) {
|
||||
return 0
|
||||
}
|
||||
return Math.min(value, count - 1)
|
||||
}
|
||||
|
||||
shared function statusFor(index) {
|
||||
if (index < activeIndex()) {
|
||||
return "complete"
|
||||
}
|
||||
if (index === activeIndex()) {
|
||||
return "current"
|
||||
}
|
||||
return "upcoming"
|
||||
}
|
||||
|
||||
// An empty orientation is how the roving runtime is told to stay out of
|
||||
// the way, so a read-only stepper never takes arrow-key focus.
|
||||
shared function rovingAxis() {
|
||||
if (!clickable) {
|
||||
return ""
|
||||
}
|
||||
return orientation === "vertical" ? "vertical" : "horizontal"
|
||||
}
|
||||
|
||||
shared function isActiveStep(index) {
|
||||
return index === activeIndex()
|
||||
}
|
||||
|
||||
shared function lastIndex() {
|
||||
return Math.max(0, stepList().length - 1)
|
||||
}
|
||||
|
||||
shared function onLastStep() {
|
||||
return activeIndex() >= lastIndex()
|
||||
}
|
||||
|
||||
shared function activeStep() {
|
||||
var list = stepList()
|
||||
return list.length ? list[activeIndex()] : {}
|
||||
}
|
||||
|
||||
client function goBack() {
|
||||
var target = Math.max(0, activeIndex() - 1)
|
||||
output.back({ index: target, step: stepList()[target] || {} })
|
||||
output.change({ index: target, step: stepList()[target] || {} })
|
||||
}
|
||||
|
||||
client function goNext() {
|
||||
if (nextDisabled) {
|
||||
return
|
||||
}
|
||||
if (onLastStep()) {
|
||||
output.finish({ index: activeIndex(), step: activeStep() })
|
||||
return
|
||||
}
|
||||
var target = Math.min(lastIndex(), activeIndex() + 1)
|
||||
output.next({ index: target, step: stepList()[target] || {} })
|
||||
output.change({ index: target, step: stepList()[target] || {} })
|
||||
}
|
||||
|
||||
client function goSkip() {
|
||||
var target = Math.min(lastIndex(), activeIndex() + 1)
|
||||
output.skip({ index: target, step: stepList()[target] || {} })
|
||||
output.change({ index: target, step: stepList()[target] || {} })
|
||||
}
|
||||
|
||||
client function selectStep(index, step) {
|
||||
if (!clickable) {
|
||||
return
|
||||
}
|
||||
output.change({ index: index, step: step })
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Stepper"
|
||||
class='wire-stepper {class}'
|
||||
data-orientation='{orientation}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-clickable='{clickable}'
|
||||
>
|
||||
<ol
|
||||
class="wire-stepper__list"
|
||||
data-wrn-roving='{rovingAxis()}'
|
||||
aria-label='{label}'
|
||||
>
|
||||
{#each stepList() as step, index}
|
||||
<li
|
||||
class="wire-stepper__step"
|
||||
data-status='{statusFor(index)}'
|
||||
aria-current='{statusFor(index) === "current" ? "step" : "false"}'
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-stepper__button"
|
||||
data-wrn-roving-item='{clickable ? "true" : "false"}'
|
||||
@click='selectStep(index, step)'
|
||||
>
|
||||
<span class="wire-stepper__marker" aria-hidden="true">
|
||||
<span class='wire-stepper__icon {step.icon}' data-show="step.icon"></span>
|
||||
<span class="wire-stepper__number" data-show="!step.icon">{index + 1}</span>
|
||||
</span>
|
||||
<span class="wire-stepper__body">
|
||||
<span class="wire-stepper__label">{step.label}</span>
|
||||
<span class="wire-stepper__description" data-show="step.description">
|
||||
{step.description}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<span class="wire-stepper__custom">
|
||||
<slot name="step-{index}"></slot>
|
||||
</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
|
||||
<div class="wire-stepper__panels" data-show="showPanel">
|
||||
{#each stepList() as step, index}
|
||||
<div
|
||||
class="wire-stepper__panel"
|
||||
data-show='isActiveStep(index)'
|
||||
aria-hidden='{index === activeIndex() ? "false" : "true"}'
|
||||
>
|
||||
<h4 class="wire-stepper__panel-title" data-show="step.title">{step.title}</h4>
|
||||
<p class="wire-stepper__panel-body" data-show="step.content">{step.content}</p>
|
||||
<slot name="panel-{index}"></slot>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="wire-stepper__controls" data-show="controls">
|
||||
<button
|
||||
type="button"
|
||||
class="wire-stepper__button-control wire-stepper__back"
|
||||
disabled='{activeIndex() === 0}'
|
||||
@click='goBack()'
|
||||
>
|
||||
{backLabel}
|
||||
</button>
|
||||
<span class="wire-stepper__controls-spacer"></span>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-stepper__button-control wire-stepper__skip"
|
||||
data-show="allowSkip && !onLastStep()"
|
||||
@click='goSkip()'
|
||||
>
|
||||
{skipLabel}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="wire-stepper__button-control wire-stepper__next"
|
||||
data-primary="true"
|
||||
disabled='{nextDisabled}'
|
||||
@click='goNext()'
|
||||
>
|
||||
{onLastStep() ? finishLabel : nextLabel}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
</nav>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-stepper {
|
||||
--stepper-accent: var(--wire-color-primary);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-stepper__list {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-stepper[data-color="secondary"] {
|
||||
--stepper-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-stepper[data-color="success"] {
|
||||
--stepper-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-stepper[data-color="danger"] {
|
||||
--stepper-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-stepper[data-color="info"] {
|
||||
--stepper-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-stepper[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-stepper[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-stepper[data-orientation="vertical"] .wire-stepper__list {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wire-stepper__step {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-width: 0;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.wire-stepper__button {
|
||||
appearance: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 0;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.wire-stepper[data-clickable="true"] .wire-stepper__button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-stepper[data-clickable="true"] .wire-stepper__button:hover {
|
||||
background: var(--wire-color-surface-soft);
|
||||
}
|
||||
|
||||
.wire-stepper__button:focus-visible {
|
||||
outline: 2px solid var(--stepper-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-stepper__marker {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: 999px;
|
||||
background: var(--wire-color-surface);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wire-stepper__step[data-status="complete"] .wire-stepper__marker {
|
||||
border-color: var(--stepper-accent);
|
||||
background: var(--stepper-accent);
|
||||
color: var(--wire-color-primary-contrast);
|
||||
}
|
||||
|
||||
.wire-stepper__step[data-status="current"] .wire-stepper__marker {
|
||||
border-color: var(--stepper-accent);
|
||||
color: var(--stepper-accent);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--stepper-accent) 22%, transparent);
|
||||
}
|
||||
|
||||
.wire-stepper__step[data-status="upcoming"] .wire-stepper__marker {
|
||||
color: var(--wire-color-text-muted);
|
||||
}
|
||||
|
||||
.wire-stepper__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-stepper__label {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wire-stepper__description {
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.wire-stepper__step[data-status="upcoming"] .wire-stepper__label {
|
||||
color: var(--wire-color-text-muted);
|
||||
}
|
||||
|
||||
.wire-stepper__panels {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.wire-stepper__panel {
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-surface);
|
||||
animation: wire-stepper-in 200ms ease both;
|
||||
}
|
||||
|
||||
.wire-stepper__panel-title {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wire-stepper__panel-body {
|
||||
margin: 0;
|
||||
color: var(--wire-color-text-muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.wire-stepper__controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.wire-stepper__controls-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.wire-stepper__button-control {
|
||||
appearance: none;
|
||||
padding: 0.45rem 0.9rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: var(--wire-color-surface);
|
||||
color: var(--wire-color-text);
|
||||
font: inherit;
|
||||
font-size: 0.86rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wire-stepper__button-control:hover:not(:disabled) {
|
||||
background: var(--wire-color-surface-soft);
|
||||
}
|
||||
|
||||
.wire-stepper__button-control:focus-visible {
|
||||
outline: 2px solid var(--stepper-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-stepper__button-control[data-primary="true"] {
|
||||
border-color: var(--stepper-accent);
|
||||
background: var(--stepper-accent);
|
||||
color: var(--wire-color-primary-contrast);
|
||||
}
|
||||
|
||||
/* A held step has to look held, or the button reads as broken. */
|
||||
.wire-stepper__button-control:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@keyframes wire-stepper-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(0.75rem);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* A horizontal stepper cannot stay side by side on a phone. */
|
||||
@media (max-width: 639px) {
|
||||
.wire-stepper__list {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+325
-53
@@ -1,90 +1,362 @@
|
||||
// Tabs -- a tablist over panels.
|
||||
//
|
||||
// <Tabs items='[{"label":"Overview","value":"overview"}]' active="overview" />
|
||||
//
|
||||
// With mode="url" the selection is mirrored into a query parameter using
|
||||
// history.pushState, so the panel swaps without a page load and the back
|
||||
// button works. The query parameter is used rather than the hash because it
|
||||
// survives a reload and does not collide with in-page anchors or Scrollspy.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Tabs {
|
||||
outputs {
|
||||
change(payload: { value: string; item: object; index: number })
|
||||
select(payload: { value: string; item: object; index: number })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Tabs"
|
||||
size: string = "default"
|
||||
items: unknown[] = []
|
||||
active: string = ""
|
||||
orientation: string = "horizontal"
|
||||
mode: string = "client"
|
||||
param: string = "tab"
|
||||
label: string = "Tabs"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state activeValue = active || (items[0] ? (items[0].value || items[0].id || "0") : "")
|
||||
state activeValue = ""
|
||||
// Which way the panel should slide in. Set on every change so the animation
|
||||
// follows the direction of travel rather than always coming from one side.
|
||||
state slideFrom = "forward"
|
||||
|
||||
functions {
|
||||
shared function itemList() {
|
||||
return Array.isArray(items) ? items : []
|
||||
}
|
||||
|
||||
shared function valueOf(item, index) {
|
||||
return String(item.value || item.id || index)
|
||||
}
|
||||
|
||||
/*
|
||||
* In url mode the query parameter is the source of truth, not local state.
|
||||
*
|
||||
* The client router owns popstate and swaps the whole page on back and
|
||||
* forward, which discards component state anyway. Reading the URL means
|
||||
* the right tab simply falls out of whatever render happens next, with no
|
||||
* listener to lose and nothing to keep in step.
|
||||
*/
|
||||
shared function currentValue() {
|
||||
if (mode === "url" && typeof window !== "undefined" && window.location) {
|
||||
var fromUrl = new URLSearchParams(window.location.search).get(param || "tab")
|
||||
return fromUrl ? fromUrl : defaultValue()
|
||||
}
|
||||
if (activeValue) {
|
||||
return activeValue
|
||||
}
|
||||
return defaultValue()
|
||||
}
|
||||
|
||||
// The selection this instance started with. The runtime falls back to it
|
||||
// when the back button lands on a URL that has no tab parameter at all.
|
||||
shared function defaultValue() {
|
||||
if (active) {
|
||||
return active
|
||||
}
|
||||
var list = itemList()
|
||||
return list.length ? valueOf(list[0], 0) : ""
|
||||
}
|
||||
|
||||
shared function isSelected(item, index) {
|
||||
return valueOf(item, index) === currentValue()
|
||||
}
|
||||
|
||||
shared function rovingAxis() {
|
||||
return orientation === "vertical" ? "vertical" : "horizontal"
|
||||
}
|
||||
|
||||
client function selectTab(item, index, sourceEvent) {
|
||||
if (item.disabled) {
|
||||
return
|
||||
}
|
||||
var value = valueOf(item, index)
|
||||
var list = itemList()
|
||||
var previous = -1
|
||||
for (var scan = 0; scan < list.length; scan += 1) {
|
||||
if (valueOf(list[scan], scan) === currentValue()) {
|
||||
previous = scan
|
||||
}
|
||||
}
|
||||
slideFrom = previous > index ? "back" : "forward"
|
||||
activeValue = value
|
||||
|
||||
if (mode === "url" && window.history && window.history.pushState) {
|
||||
var url = new URL(window.location.href)
|
||||
url.searchParams.set(param || "tab", value)
|
||||
window.history.pushState({}, "", url.toString())
|
||||
}
|
||||
|
||||
output.change({ value: value, item: item, index: index })
|
||||
output.select({ value: value, item: item, index: index })
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<section
|
||||
{...attrs}
|
||||
data-ui-component="Tabs"
|
||||
class='w-full {class}'
|
||||
class:flex='orientation === "vertical"'
|
||||
class:items-start='orientation === "vertical"'
|
||||
class:gap-6='orientation === "vertical"'
|
||||
class='wire-tabs {class}'
|
||||
data-orientation='{orientation}'
|
||||
data-color='{color}'
|
||||
data-size='{size}'
|
||||
data-slide='{slideFrom}'
|
||||
data-mode='{mode}'
|
||||
data-param='{param}'
|
||||
>
|
||||
<div
|
||||
class="wire-tabs__list"
|
||||
role="tablist"
|
||||
aria-label='{label}'
|
||||
aria-orientation='{orientation}'
|
||||
class="flex min-w-0 gap-1 overflow-x-auto rounded-xl border border-[var(--wire-color-border)] bg-[var(--wire-color-surface-soft)] p-1"
|
||||
class:flex-col='orientation === "vertical"'
|
||||
class:w-56='orientation === "vertical"'
|
||||
class:shrink-0='orientation === "vertical"'
|
||||
data-wrn-roving='{rovingAxis()}'
|
||||
>
|
||||
{#each items as item, index}
|
||||
{#each itemList() as item, index}
|
||||
<button
|
||||
type="button"
|
||||
class="wire-tabs__tab"
|
||||
role="tab"
|
||||
id='tab-{item.value || item.id || index}'
|
||||
aria-selected='{activeValue === (item.value || item.id || String(index))}'
|
||||
aria-controls='panel-{item.value || item.id || index}'
|
||||
tabindex='{activeValue === (item.value || item.id || String(index)) ? "0" : "-1"}'
|
||||
disabled='{item.disabled || false}'
|
||||
class="inline-flex min-w-max flex-1 items-center justify-center gap-2 rounded-lg px-4 py-2.5 text-sm font-semibold outline-none transition disabled:cursor-not-allowed disabled:opacity-50"
|
||||
class:bg-[var(--wire-color-surface-raised)]='activeValue === (item.value || item.id || String(index))'
|
||||
class:text-[var(--wire-color-primary)]='activeValue === (item.value || item.id || String(index))'
|
||||
class:shadow-sm='activeValue === (item.value || item.id || String(index))'
|
||||
class:text-[var(--wire-color-text-muted)]='activeValue !== (item.value || item.id || String(index))'
|
||||
class:hover:text-[var(--wire-color-text)]='activeValue !== (item.value || item.id || String(index))'
|
||||
class:justify-start='orientation === "vertical"'
|
||||
class:px-3='size === "sm"'
|
||||
class:py-2='size === "sm"'
|
||||
class:px-5='size === "lg"'
|
||||
class:py-3='size === "lg"'
|
||||
@click='activeValue = item.value || item.id || String(index); event.currentTarget.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: { item: item, index: index, value: activeValue } })); event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: item, index: index, value: activeValue } }))'
|
||||
data-value='{valueOf(item, index)}'
|
||||
data-wrn-roving-item="true"
|
||||
id='wire-tab-{valueOf(item, index)}'
|
||||
aria-controls='wire-panel-{valueOf(item, index)}'
|
||||
aria-selected='{isSelected(item, index) ? "true" : "false"}'
|
||||
aria-disabled='{item.disabled ? "true" : "false"}'
|
||||
@click='selectTab(item, index, event)'
|
||||
>
|
||||
{#if item.icon}
|
||||
<span class='{item.icon + " size-4"}' aria-hidden="true"></span>
|
||||
{/if}
|
||||
<span>{item.label || item.title}</span>
|
||||
{#if item.badge}
|
||||
<span class="rounded-full bg-[var(--wire-color-primary-soft)] px-2 py-0.5 text-xs text-[var(--wire-color-primary)]">{item.badge}</span>
|
||||
{/if}
|
||||
<span class='wire-tabs__icon {item.icon}' data-show="item.icon" aria-hidden="true"></span>
|
||||
<span class="wire-tabs__label">{item.label || item.title}</span>
|
||||
<span class="wire-tabs__badge" data-show="item.badge">{item.badge}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 flex-1 pt-4" class:pt-0='orientation === "vertical"'>
|
||||
{#each items as item, index}
|
||||
<div class="wire-tabs__panels">
|
||||
{#each itemList() as item, index}
|
||||
<div
|
||||
class="wire-tabs__panel"
|
||||
role="tabpanel"
|
||||
id='panel-{item.value || item.id || index}'
|
||||
aria-labelledby='tab-{item.value || item.id || index}'
|
||||
id='wire-panel-{valueOf(item, index)}'
|
||||
aria-labelledby='wire-tab-{valueOf(item, index)}'
|
||||
tabindex="0"
|
||||
data-show='activeValue === (item.value || item.id || String(index))'
|
||||
class="rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-[var(--wire-color-focus)]"
|
||||
data-show='isSelected(item, index)'
|
||||
>
|
||||
{#if item.title && item.title !== item.label}
|
||||
<h3 class="text-lg font-bold text-[var(--wire-color-text)]">{item.title}</h3>
|
||||
{/if}
|
||||
{#if item.description}
|
||||
<p class="mt-2 leading-7 text-[var(--wire-color-text-muted)]">{item.description}</p>
|
||||
{/if}
|
||||
{#if item.content}
|
||||
<div class="mt-4 text-[var(--wire-color-text)]">{item.content}</div>
|
||||
{/if}
|
||||
<h3 class="wire-tabs__title" data-show="item.title && item.title !== item.label">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p class="wire-tabs__description" data-show="item.description">{item.description}</p>
|
||||
<div class="wire-tabs__content" data-show="item.content">{item.content}</div>
|
||||
<slot name="panel-{valueOf(item, index)}"></slot>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<slot></slot>
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-tabs {
|
||||
--tabs-accent: var(--wire-color-primary);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wire-tabs[data-color="secondary"] {
|
||||
--tabs-accent: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-tabs[data-color="success"] {
|
||||
--tabs-accent: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-tabs[data-color="danger"] {
|
||||
--tabs-accent: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-tabs[data-color="info"] {
|
||||
--tabs-accent: var(--wire-color-info);
|
||||
}
|
||||
|
||||
.wire-tabs[data-size="sm"] {
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.wire-tabs[data-size="lg"] {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-tabs[data-orientation="vertical"] {
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.wire-tabs__list {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
min-width: 0;
|
||||
padding: 0.25rem;
|
||||
border: 1px solid var(--wire-color-border);
|
||||
border-radius: var(--wire-radius-md);
|
||||
background: var(--wire-color-surface-soft);
|
||||
/* A long tablist scrolls rather than wrapping into an unusable stack. */
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.wire-tabs[data-orientation="vertical"] .wire-tabs__list {
|
||||
flex-direction: column;
|
||||
flex: 0 0 auto;
|
||||
width: 14rem;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.wire-tabs__tab {
|
||||
appearance: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4rem;
|
||||
flex: 1 0 auto;
|
||||
padding: 0.55rem 0.9rem;
|
||||
border: 0;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
background: transparent;
|
||||
color: var(--wire-color-text-muted);
|
||||
font: inherit;
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 160ms ease,
|
||||
color 160ms ease;
|
||||
}
|
||||
|
||||
.wire-tabs[data-orientation="vertical"] .wire-tabs__tab {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.wire-tabs__tab:hover {
|
||||
color: var(--wire-color-text);
|
||||
}
|
||||
|
||||
.wire-tabs__tab:focus-visible {
|
||||
outline: 2px solid var(--tabs-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.wire-tabs__tab[aria-selected="true"] {
|
||||
background: var(--wire-color-surface);
|
||||
color: var(--tabs-accent);
|
||||
box-shadow: var(--wire-shadow-1);
|
||||
}
|
||||
|
||||
.wire-tabs__tab[aria-disabled="true"] {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.wire-tabs__badge {
|
||||
padding: 0.05rem 0.4rem;
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--tabs-accent) 16%, transparent);
|
||||
color: var(--tabs-accent);
|
||||
font-size: 0.72rem;
|
||||
}
|
||||
|
||||
.wire-tabs__panels {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.wire-tabs__panel {
|
||||
outline: none;
|
||||
animation: wire-tabs-slide-forward 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
||||
}
|
||||
|
||||
.wire-tabs[data-slide="back"] .wire-tabs__panel {
|
||||
animation-name: wire-tabs-slide-back;
|
||||
}
|
||||
|
||||
/* The panels clip their own slide so it never widens the page. */
|
||||
.wire-tabs__panels {
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
.wire-tabs__panel:focus-visible {
|
||||
outline: 2px solid var(--tabs-accent);
|
||||
outline-offset: 4px;
|
||||
border-radius: var(--wire-radius-sm);
|
||||
}
|
||||
|
||||
.wire-tabs__title {
|
||||
margin: 0 0 0.35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.wire-tabs__description {
|
||||
margin: 0;
|
||||
color: var(--wire-color-text-muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.wire-tabs__content {
|
||||
margin-top: 0.6rem;
|
||||
}
|
||||
|
||||
@keyframes wire-tabs-slide-forward {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(1.25rem);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wire-tabs-slide-back {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-1.25rem);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Respect a reduced-motion preference: swap instantly instead. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wire-tabs__panel {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 639px) {
|
||||
.wire-tabs[data-orientation="vertical"] {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wire-tabs[data-orientation="vertical"] .wire-tabs__list {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
component Timeline {
|
||||
outputs {
|
||||
select(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
@@ -30,7 +34,7 @@ component Timeline {
|
||||
{#each items as item, index}
|
||||
<li
|
||||
class="relative pb-8 pl-8 last:pb-0"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("select", { bubbles: true, detail: { item: item, index: index } }))'
|
||||
@click='output.select({ item: item, index: index })'
|
||||
>
|
||||
<span
|
||||
class="absolute -left-[1.05rem] top-0 flex size-8 items-center justify-center rounded-full border-4 border-[var(--wire-color-background)] bg-[var(--wire-color-primary)] text-[var(--wire-color-on-primary)] shadow-sm"
|
||||
|
||||
@@ -57,34 +57,17 @@ component ToggleCount {
|
||||
: emptyValue
|
||||
}
|
||||
|
||||
client function dispatchToggleEvent(sourceEvent, previousValue, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-toggle-count]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
client function dispatchToggleEvent(sourceEvent, previousValue, payload) {
|
||||
payload = {
|
||||
component: "ToggleCount",
|
||||
name: name,
|
||||
value: selectedValue,
|
||||
previousValue: previousValue,
|
||||
firstValue: firstValue,
|
||||
secondValue: secondValue
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("change", true, false, {
|
||||
component: "ToggleCount",
|
||||
name: name,
|
||||
value: selectedValue,
|
||||
previousValue: previousValue,
|
||||
firstValue: firstValue,
|
||||
secondValue: secondValue
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("toggle", true, false, {
|
||||
component: "ToggleCount",
|
||||
name: name,
|
||||
value: selectedValue,
|
||||
previousValue: previousValue,
|
||||
firstValue: firstValue,
|
||||
secondValue: secondValue
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
output.change(payload)
|
||||
output.toggle(payload)
|
||||
}
|
||||
|
||||
client function selectValue(sourceEvent, nextValue, previousValue) {
|
||||
|
||||
@@ -11,26 +11,74 @@ component Typography {
|
||||
view {
|
||||
<div
|
||||
data-ui-component="Typography"
|
||||
class='wire-typography text-[var(--wire-color-text)] {class}'
|
||||
class:max-w-3xl='maxWidth === "md"'
|
||||
class:max-w-5xl='maxWidth === "lg"'
|
||||
class:max-w-7xl='maxWidth === "xl"'
|
||||
class:max-w-none='maxWidth === "full"'
|
||||
class:text-sm='size === "sm"'
|
||||
class:text-base='size === "default" || size === "md"'
|
||||
class:text-lg='size === "lg"'
|
||||
class:columns-1='columns === 1'
|
||||
class:md:columns-2='columns === 2'
|
||||
class:lg:columns-3='columns === 3'
|
||||
class:gap-4='gap === "sm"'
|
||||
class:gap-8='gap === "md"'
|
||||
class:gap-12='gap === "lg"'
|
||||
class='wire-typography {class}'
|
||||
data-size='{size}'
|
||||
data-columns='{columns}'
|
||||
data-gap='{gap}'
|
||||
data-max-width='{maxWidth}'
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-typography {
|
||||
color: var(--wire-color-text);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-size="sm"] {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-size="lg"] {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-max-width="md"] {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-max-width="lg"] {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-max-width="xl"] {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-max-width="full"] {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Multi-column prose only below a comfortable reading width; a narrow
|
||||
* screen split into columns is unreadable.
|
||||
*/
|
||||
.wire-typography[data-gap="sm"] {
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-gap="md"] {
|
||||
column-gap: 2rem;
|
||||
}
|
||||
|
||||
.wire-typography[data-gap="lg"] {
|
||||
column-gap: 3rem;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.wire-typography[data-columns="2"] {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wire-typography[data-columns="3"] {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
.wire-typography :where(h1, h2, h3, h4) {
|
||||
color: var(--wire-color-text);
|
||||
font-weight: 700;
|
||||
|
||||
@@ -35,22 +35,23 @@ component Alert {
|
||||
state visible: boolean = true
|
||||
|
||||
functions {
|
||||
client function dispatchAlertEvent(sourceEvent, eventName, action, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-alert]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent(eventName, true, false, {
|
||||
// The output name has to be written out rather than computed: outputs are
|
||||
// resolved as named properties, so a dynamic key would not reach a parent
|
||||
// binding. Only two names exist here, so a branch is honest and cheap.
|
||||
client function dispatchAlertEvent(sourceEvent, eventName, action, payload) {
|
||||
payload = {
|
||||
component: "Alert",
|
||||
title: title,
|
||||
color: color,
|
||||
variant: variant,
|
||||
action: action
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
}
|
||||
|
||||
if (eventName === "dismiss") {
|
||||
output.dismiss(payload)
|
||||
} else {
|
||||
output.action(payload)
|
||||
}
|
||||
}
|
||||
|
||||
client function dismissAlert(sourceEvent) {
|
||||
|
||||
@@ -35,15 +35,13 @@ component Badge {
|
||||
state visible: boolean = true
|
||||
|
||||
functions {
|
||||
client function dismissBadge(sourceEvent, root, customEvent) {
|
||||
// output.dismiss reaches a parent @dismiss binding; a raw dispatchEvent on
|
||||
// the root does not. The runtime registers parent handlers in a registry
|
||||
// that only the output proxy consults, so the CustomEvent this used to
|
||||
// build bubbled past every binding and was never seen by anyone.
|
||||
client function dismissBadge() {
|
||||
visible = false
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-badge]")
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("dismiss", true, false, {
|
||||
component: "Badge",
|
||||
label: label
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
output.dismiss({ component: "Badge", label: label })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,67 +45,27 @@ component Card {
|
||||
state dismissed: boolean = false
|
||||
|
||||
functions {
|
||||
client function dispatchCardNavigation(sourceEvent, item, index, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-card]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("navigate", true, false, {
|
||||
component: "Card",
|
||||
item: item,
|
||||
index: index
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
// Outputs must go through output.*; a CustomEvent dispatched on the root
|
||||
// bubbles past every parent @binding without being seen, because the
|
||||
// runtime keeps parent handlers in a registry only the output proxy reads.
|
||||
client function dispatchCardNavigation(sourceEvent, item, index) {
|
||||
output.navigate({ component: "Card", item: item, index: index })
|
||||
}
|
||||
|
||||
client function dispatchCardNavigationValue(sourceEvent, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-card]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("navigate", true, false, {
|
||||
client function dispatchCardNavigationValue(sourceEvent) {
|
||||
output.navigate({
|
||||
component: "Card",
|
||||
value: sourceEvent.currentTarget.value
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
}
|
||||
|
||||
client function dispatchCardHeaderAction(sourceEvent, action, index, root, customEvent) {
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-card]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("action", true, false, {
|
||||
component: "Card",
|
||||
action: action,
|
||||
index: index
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
client function dispatchCardHeaderAction(sourceEvent, action, index) {
|
||||
output.action({ component: "Card", action: action, index: index })
|
||||
}
|
||||
|
||||
client function dismissCard(sourceEvent, root, customEvent) {
|
||||
client function dismissCard() {
|
||||
dismissed = true
|
||||
root = sourceEvent.currentTarget.closest("[data-wrn-card]")
|
||||
|
||||
if (!root) {
|
||||
return
|
||||
}
|
||||
|
||||
customEvent = document.createEvent("CustomEvent")
|
||||
customEvent.initCustomEvent("dismiss", true, false, {
|
||||
component: "Card",
|
||||
title: title
|
||||
})
|
||||
root.dispatchEvent(customEvent)
|
||||
output.dismiss({ component: "Card", title: title })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +97,8 @@ component Card {
|
||||
alt='{item.imageAlt || item.alt || ""}'
|
||||
loading='{item.loading || "lazy"}'
|
||||
decoding="async"
|
||||
@load='event.currentTarget.dispatchEvent(new CustomEvent("load", { bubbles: true, detail: { item: item, index: itemIndex } }))'
|
||||
@error='event.currentTarget.dispatchEvent(new CustomEvent("error", { bubbles: true, detail: { item: item, index: itemIndex } }))'
|
||||
@load='output.load({ item: item, index: itemIndex })'
|
||||
@error='output.error({ item: item, index: itemIndex })'
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -157,7 +117,7 @@ component Card {
|
||||
<a
|
||||
href='{item.actionHref || item.href || "#"}'
|
||||
class="wire-next__card-action"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("action", { bubbles: true, detail: { item: item, index: itemIndex } }))'
|
||||
@click='output.action({ item: item, index: itemIndex })'
|
||||
>
|
||||
<span>{item.actionLabel || "Learn more"}</span>
|
||||
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
|
||||
@@ -180,8 +140,8 @@ component Card {
|
||||
alt='{imageAlt}'
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@load='event.currentTarget.dispatchEvent(new CustomEvent("load", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@error='event.currentTarget.dispatchEvent(new CustomEvent("error", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@load='output.load({ src: imageSrc })'
|
||||
@error='output.error({ src: imageSrc })'
|
||||
/>
|
||||
<div class="wire-next__card-overlay-shade" aria-hidden="true"></div>
|
||||
</div>
|
||||
@@ -194,8 +154,8 @@ component Card {
|
||||
alt='{imageAlt}'
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@load='event.currentTarget.dispatchEvent(new CustomEvent("load", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@error='event.currentTarget.dispatchEvent(new CustomEvent("error", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@load='output.load({ src: imageSrc })'
|
||||
@error='output.error({ src: imageSrc })'
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -323,7 +283,7 @@ component Card {
|
||||
<a
|
||||
href='{actionHref || "#"}'
|
||||
class="wire-next__card-action"
|
||||
@click='event.currentTarget.dispatchEvent(new CustomEvent("action", { bubbles: true, detail: { href: actionHref, label: actionLabel } }))'
|
||||
@click='output.action({ href: actionHref, label: actionLabel })'
|
||||
>
|
||||
<span>{actionLabel}</span>
|
||||
<span class="icon-[lucide--arrow-right]" aria-hidden="true"></span>
|
||||
@@ -340,8 +300,8 @@ component Card {
|
||||
alt='{imageAlt}'
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
@load='event.currentTarget.dispatchEvent(new CustomEvent("load", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@error='event.currentTarget.dispatchEvent(new CustomEvent("error", { bubbles: true, detail: { src: imageSrc } }))'
|
||||
@load='output.load({ src: imageSrc })'
|
||||
@error='output.error({ src: imageSrc })'
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,46 +1,162 @@
|
||||
// Container -- a centred, width-limited page wrapper.
|
||||
//
|
||||
// <Container maxWidth="lg">...</Container>
|
||||
//
|
||||
// It also accepts columns and gap, because it always has and applications
|
||||
// depend on it. Reach for Grid when a grid is the point; reach for Container
|
||||
// when the point is the reading width.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Container {
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
// Stays 2 to match what shipped. A published default cannot be changed
|
||||
// without silently reflowing every Container already in use.
|
||||
columns: number = 2
|
||||
gap: string = "md"
|
||||
maxWidth: string = "xl"
|
||||
centered: boolean = true
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function columnCount() {
|
||||
var value = Number(columns)
|
||||
if (!value || value < 1) {
|
||||
return 1
|
||||
}
|
||||
return Math.min(6, value)
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Container"
|
||||
class='wire-container {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-columns='{columns}'
|
||||
data-gap='{gap}'
|
||||
data-max-width='{maxWidth}'
|
||||
class='mx-auto w-full {class}'
|
||||
class:max-w-3xl='maxWidth === "md"'
|
||||
class:max-w-5xl='maxWidth === "lg"'
|
||||
class:max-w-7xl='maxWidth === "xl" || maxWidth === "wide"'
|
||||
class:max-w-screen-2xl='maxWidth === "2xl"'
|
||||
class:max-w-none='maxWidth === "full"'
|
||||
class:px-4='size === "compact" || size === "default"'
|
||||
class:px-5='size === "comfortable"'
|
||||
class:px-6='size === "spacious"'
|
||||
class:sm:px-6='size !== "compact"'
|
||||
class:lg:px-8='size === "default" || size === "comfortable" || size === "spacious"'
|
||||
class:grid='columns > 1'
|
||||
class:grid-cols-1='columns > 1'
|
||||
class:sm:grid-cols-2='columns === 2 || columns === 3 || columns === 4 || columns === 5 || columns === 6'
|
||||
class:lg:grid-cols-3='columns === 3'
|
||||
class:lg:grid-cols-4='columns === 4'
|
||||
class:lg:grid-cols-5='columns === 5'
|
||||
class:lg:grid-cols-6='columns === 6'
|
||||
class:gap-2='gap === "xs"'
|
||||
class:gap-3='gap === "sm"'
|
||||
class:gap-5='gap === "md"'
|
||||
class:gap-8='gap === "lg"'
|
||||
class:gap-10='gap === "xl"'
|
||||
data-centered='{centered}'
|
||||
data-columns='{columnCount()}'
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-container {
|
||||
--container-gap: 1.25rem;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
.wire-container[data-centered="true"] {
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
/* Reading widths, not breakpoints: the cap is about line length. */
|
||||
.wire-container[data-max-width="md"] {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.wire-container[data-max-width="lg"] {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.wire-container[data-max-width="xl"],
|
||||
.wire-container[data-max-width="wide"] {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
.wire-container[data-max-width="2xl"] {
|
||||
max-width: 96rem;
|
||||
}
|
||||
|
||||
.wire-container[data-max-width="full"] {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wire-container[data-size="compact"] {
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
.wire-container[data-size="comfortable"] {
|
||||
padding-inline: 1.25rem;
|
||||
}
|
||||
|
||||
.wire-container[data-size="spacious"] {
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
.wire-container[data-gap="xs"] {
|
||||
--container-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.wire-container[data-gap="sm"] {
|
||||
--container-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.wire-container[data-gap="lg"] {
|
||||
--container-gap: 2rem;
|
||||
}
|
||||
|
||||
.wire-container[data-gap="xl"] {
|
||||
--container-gap: 2.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Columns are opt-in. One column stays plain flow so a container does not
|
||||
* quietly turn every page into a grid.
|
||||
*/
|
||||
.wire-container:not([data-columns="1"]) {
|
||||
display: grid;
|
||||
gap: var(--container-gap);
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.wire-container[data-columns="2"],
|
||||
.wire-container[data-columns="3"],
|
||||
.wire-container[data-columns="4"],
|
||||
.wire-container[data-columns="5"],
|
||||
.wire-container[data-columns="6"] {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-container[data-size="default"],
|
||||
.wire-container[data-size="comfortable"],
|
||||
.wire-container[data-size="spacious"] {
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wire-container[data-columns="3"] {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-container[data-columns="4"] {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-container[data-columns="5"] {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-container[data-columns="6"] {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-container[data-size="default"],
|
||||
.wire-container[data-size="comfortable"],
|
||||
.wire-container[data-size="spacious"] {
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,141 @@
|
||||
// Divider -- a rule between sections, optionally labelled.
|
||||
//
|
||||
// <Divider />
|
||||
// <Divider label="or" />
|
||||
// <Divider orientation="vertical" />
|
||||
//
|
||||
// The label sits between two rules rather than on top of one, so the text
|
||||
// never overlaps the line at any width.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Divider {
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = ""
|
||||
orientation: string = "horizontal"
|
||||
variant: string = "solid"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function isVertical() {
|
||||
return orientation === "vertical"
|
||||
}
|
||||
|
||||
// The rule only takes the component colour when there is a label. A plain
|
||||
// divider is chrome and should stay the border token.
|
||||
shared function tone() {
|
||||
return label ? color : "border"
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Divider"
|
||||
class='wire-divider {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-tone='{tone()}'
|
||||
data-variant='{variant}'
|
||||
data-orientation='{orientation}'
|
||||
class='w-full {class}'
|
||||
class:h-full='orientation === "vertical"'
|
||||
class:w-auto='orientation === "vertical"'
|
||||
data-labelled='{label ? "true" : "false"}'
|
||||
role="separator"
|
||||
aria-orientation='{isVertical() ? "vertical" : "horizontal"}'
|
||||
>
|
||||
{#if orientation === "vertical"}
|
||||
<div
|
||||
role="separator"
|
||||
aria-orientation="vertical"
|
||||
class="mx-3 h-full min-h-6 border-l border-[var(--wire-color-border)]"
|
||||
class:border-l-2='size === "lg"'
|
||||
class:border-[var(--wire-color-primary)]='color === "primary"'
|
||||
class:border-[var(--wire-color-secondary)]='color === "secondary"'
|
||||
class:border-[var(--wire-color-success)]='color === "success"'
|
||||
class:border-[var(--wire-color-warning)]='color === "warning"'
|
||||
class:border-[var(--wire-color-danger)]='color === "danger"'
|
||||
></div>
|
||||
{:else}
|
||||
<div
|
||||
role="separator"
|
||||
aria-orientation="horizontal"
|
||||
class="flex w-full items-center gap-4"
|
||||
>
|
||||
<span
|
||||
class="h-px flex-1 bg-[var(--wire-color-border)]"
|
||||
class:h-0.5='size === "lg"'
|
||||
class:bg-[var(--wire-color-primary)]='color === "primary" && label !== ""'
|
||||
class:bg-[var(--wire-color-secondary)]='color === "secondary" && label !== ""'
|
||||
class:bg-[var(--wire-color-success)]='color === "success" && label !== ""'
|
||||
class:bg-[var(--wire-color-warning)]='color === "warning" && label !== ""'
|
||||
class:bg-[var(--wire-color-danger)]='color === "danger" && label !== ""'
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
|
||||
{#if label}
|
||||
<span class="shrink-0 text-xs font-bold uppercase tracking-[0.16em] text-[var(--wire-color-text-muted)]">
|
||||
{label}
|
||||
</span>
|
||||
<span
|
||||
class="h-px flex-1 bg-[var(--wire-color-border)]"
|
||||
class:h-0.5='size === "lg"'
|
||||
class:bg-[var(--wire-color-primary)]='color === "primary"'
|
||||
class:bg-[var(--wire-color-secondary)]='color === "secondary"'
|
||||
class:bg-[var(--wire-color-success)]='color === "success"'
|
||||
class:bg-[var(--wire-color-warning)]='color === "warning"'
|
||||
class:bg-[var(--wire-color-danger)]='color === "danger"'
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<span class="wire-divider__rule" aria-hidden="true"></span>
|
||||
<span class="wire-divider__label" data-show="label">{label}</span>
|
||||
<span class="wire-divider__rule" data-show="label" aria-hidden="true"></span>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-divider {
|
||||
--divider-tone: var(--wire-color-border);
|
||||
--divider-thickness: 1px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.wire-divider[data-tone="primary"] {
|
||||
--divider-tone: var(--wire-color-primary);
|
||||
}
|
||||
|
||||
.wire-divider[data-tone="secondary"] {
|
||||
--divider-tone: var(--wire-color-secondary);
|
||||
}
|
||||
|
||||
.wire-divider[data-tone="success"] {
|
||||
--divider-tone: var(--wire-color-success);
|
||||
}
|
||||
|
||||
.wire-divider[data-tone="warning"] {
|
||||
--divider-tone: var(--wire-color-warning);
|
||||
}
|
||||
|
||||
.wire-divider[data-tone="danger"] {
|
||||
--divider-tone: var(--wire-color-danger);
|
||||
}
|
||||
|
||||
.wire-divider[data-size="lg"] {
|
||||
--divider-thickness: 2px;
|
||||
}
|
||||
|
||||
.wire-divider__rule {
|
||||
flex: 1 1 auto;
|
||||
height: var(--divider-thickness);
|
||||
background: var(--divider-tone);
|
||||
}
|
||||
|
||||
.wire-divider[data-variant="dashed"] .wire-divider__rule {
|
||||
height: 0;
|
||||
background: transparent;
|
||||
border-top: var(--divider-thickness) dashed var(--divider-tone);
|
||||
}
|
||||
|
||||
.wire-divider[data-variant="dotted"] .wire-divider__rule {
|
||||
height: 0;
|
||||
background: transparent;
|
||||
border-top: var(--divider-thickness) dotted var(--divider-tone);
|
||||
}
|
||||
|
||||
.wire-divider__label {
|
||||
flex: 0 0 auto;
|
||||
color: var(--wire-color-text-muted);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wire-divider[data-orientation="vertical"] {
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
min-height: 1.5rem;
|
||||
margin-inline: 0.75rem;
|
||||
}
|
||||
|
||||
.wire-divider[data-orientation="vertical"] .wire-divider__rule {
|
||||
width: var(--divider-thickness);
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.wire-divider[data-orientation="vertical"][data-variant="dashed"] .wire-divider__rule {
|
||||
width: 0;
|
||||
border-top: 0;
|
||||
border-left: var(--divider-thickness) dashed var(--divider-tone);
|
||||
}
|
||||
|
||||
.wire-divider[data-orientation="vertical"][data-variant="dotted"] .wire-divider__rule {
|
||||
width: 0;
|
||||
border-top: 0;
|
||||
border-left: var(--divider-thickness) dotted var(--divider-tone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
-19
@@ -1,3 +1,13 @@
|
||||
// Grid -- an equal-track grid that collapses on small screens.
|
||||
//
|
||||
// <Grid columns={3} gap="lg">...</Grid>
|
||||
//
|
||||
// Pass minItemWidth to let the browser decide the count instead: the track
|
||||
// list becomes auto-fit, which reflows continuously rather than at fixed
|
||||
// breakpoints and suits card decks whose item count is not known.
|
||||
//
|
||||
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
||||
// and silently swallows the rule that follows it.
|
||||
component Grid {
|
||||
props {
|
||||
size: string = "default"
|
||||
@@ -5,36 +15,124 @@ component Grid {
|
||||
columns: number = 2
|
||||
gap: string = "md"
|
||||
maxWidth: string = "xl"
|
||||
// When set, the count is derived from the available width instead of the
|
||||
// columns prop. Accepts any CSS length.
|
||||
minItemWidth: string = ""
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
functions {
|
||||
shared function columnCount() {
|
||||
var value = Number(columns)
|
||||
if (!value || value < 1) {
|
||||
return 1
|
||||
}
|
||||
return Math.min(6, value)
|
||||
}
|
||||
|
||||
shared function autoTrack() {
|
||||
return minItemWidth ? "repeat(auto-fit, minmax(" + minItemWidth + ", 1fr))" : ""
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<div
|
||||
{...attrs}
|
||||
data-ui-component="Grid"
|
||||
class='wire-grid {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-columns='{columns}'
|
||||
data-gap='{gap}'
|
||||
class='grid w-full grid-cols-1 {class}'
|
||||
class:max-w-3xl='maxWidth === "md"'
|
||||
class:max-w-5xl='maxWidth === "lg"'
|
||||
class:max-w-7xl='maxWidth === "xl"'
|
||||
class:max-w-screen-2xl='maxWidth === "2xl"'
|
||||
class:max-w-none='maxWidth === "full"'
|
||||
class:sm:grid-cols-2='columns >= 2'
|
||||
class:lg:grid-cols-3='columns === 3'
|
||||
class:lg:grid-cols-4='columns === 4'
|
||||
class:lg:grid-cols-5='columns === 5'
|
||||
class:lg:grid-cols-6='columns === 6'
|
||||
class:gap-2='gap === "xs"'
|
||||
class:gap-3='gap === "sm"'
|
||||
class:gap-5='gap === "md"'
|
||||
class:gap-8='gap === "lg"'
|
||||
class:gap-10='gap === "xl"'
|
||||
class:items-start='size === "compact"'
|
||||
class:items-stretch='size !== "compact"'
|
||||
data-max-width='{maxWidth}'
|
||||
data-columns='{columnCount()}'
|
||||
data-auto='{minItemWidth ? "true" : "false"}'
|
||||
style='--grid-auto-track:{autoTrack()};'
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-grid {
|
||||
--grid-gap: 1.25rem;
|
||||
display: grid;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
gap: var(--grid-gap);
|
||||
grid-template-columns: 1fr;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.wire-grid[data-size="compact"] {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.wire-grid[data-max-width="md"] {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-max-width="lg"] {
|
||||
max-width: 64rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-max-width="xl"] {
|
||||
max-width: 80rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-max-width="2xl"] {
|
||||
max-width: 96rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-max-width="full"] {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.wire-grid[data-gap="xs"] {
|
||||
--grid-gap: 0.5rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-gap="sm"] {
|
||||
--grid-gap: 0.75rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-gap="lg"] {
|
||||
--grid-gap: 2rem;
|
||||
}
|
||||
|
||||
.wire-grid[data-gap="xl"] {
|
||||
--grid-gap: 2.5rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* auto-fit needs no breakpoints, so it wins over the fixed counts below
|
||||
* and applies at every width.
|
||||
*/
|
||||
.wire-grid[data-auto="true"] {
|
||||
grid-template-columns: var(--grid-auto-track);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.wire-grid[data-auto="false"]:not([data-columns="1"]) {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.wire-grid[data-auto="false"][data-columns="3"] {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-grid[data-auto="false"][data-columns="4"] {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-grid[data-auto="false"][data-columns="5"] {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.wire-grid[data-auto="false"][data-columns="6"] {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "@wrnexus/ui — part of the WrNexus framework.",
|
||||
"license": "MIT",
|
||||
@@ -46,7 +46,7 @@
|
||||
"./ui.css": "./ui.css"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
-2012
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Secure upload drivers, policies, client runtime, helper functions, and reusable upload components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6",
|
||||
"@wrnexus/ui": "^0.8.6"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Shared server/browser schemas, validation helpers, form runtime, and reusable error components.",
|
||||
"license": "MIT",
|
||||
@@ -42,9 +42,9 @@
|
||||
"./components/*": "./components/*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "^0.8.5",
|
||||
"@wrnexus/plugin": "^0.8.5",
|
||||
"@wrnexus/ui": "^0.8.5"
|
||||
"@wrnexus/core": "^0.8.6",
|
||||
"@wrnexus/plugin": "^0.8.6",
|
||||
"@wrnexus/ui": "^0.8.6"
|
||||
},
|
||||
"wrnexus": {
|
||||
"plugin": {
|
||||
|
||||
@@ -107,11 +107,11 @@
|
||||
},
|
||||
"packages/ai": {
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/auth": {
|
||||
"name": "@wrnexus/auth",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
"@wrnexus/captcha": "workspace:*",
|
||||
@@ -133,7 +133,7 @@
|
||||
},
|
||||
"packages/authz": {
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/db": "workspace:*",
|
||||
@@ -141,18 +141,18 @@
|
||||
},
|
||||
"packages/benchmark": {
|
||||
"name": "@wrnexus/benchmark",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/cache": {
|
||||
"name": "@wrnexus/cache",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/captcha": {
|
||||
"name": "@wrnexus/captcha",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
@@ -167,7 +167,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"bin": {
|
||||
"wrnexus": "src/index.ts",
|
||||
},
|
||||
@@ -194,7 +194,7 @@
|
||||
},
|
||||
"packages/compiler": {
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/csr": "workspace:*",
|
||||
"@wrnexus/store": "workspace:*",
|
||||
@@ -204,7 +204,7 @@
|
||||
},
|
||||
"packages/content": {
|
||||
"name": "@wrnexus/content",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"typescript": "^5.9.2",
|
||||
@@ -212,18 +212,18 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/csr": {
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/db": {
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"typescript": "^5.9.2",
|
||||
@@ -231,7 +231,7 @@
|
||||
},
|
||||
"packages/dev-server": {
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
"@wrnexus/cache": "workspace:*",
|
||||
@@ -258,7 +258,7 @@
|
||||
},
|
||||
"packages/dev-toolbar": {
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"typescript": "^5.9.2",
|
||||
@@ -266,7 +266,7 @@
|
||||
},
|
||||
"packages/encryption": {
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
@@ -277,21 +277,21 @@
|
||||
},
|
||||
"packages/graphql": {
|
||||
"name": "@wrnexus/graphql",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/helpers": {
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/i18n": {
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
@@ -305,7 +305,7 @@
|
||||
},
|
||||
"packages/identity": {
|
||||
"name": "@wrnexus/identity",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/auth": "workspace:*",
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
@@ -314,7 +314,7 @@
|
||||
},
|
||||
"packages/image": {
|
||||
"name": "@wrnexus/image",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
"@wrnexus/security": "workspace:*",
|
||||
@@ -334,7 +334,7 @@
|
||||
},
|
||||
"packages/jwt": {
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
@@ -345,7 +345,7 @@
|
||||
},
|
||||
"packages/language-server": {
|
||||
"name": "@wrnexus/language-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"bin": {
|
||||
"wrnexus-language-server": "src/server.ts",
|
||||
},
|
||||
@@ -356,39 +356,39 @@
|
||||
},
|
||||
"packages/mcp": {
|
||||
"name": "@wrnexus/mcp",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"bin": {
|
||||
"wrnexus-mcp": "src/stdio.ts",
|
||||
},
|
||||
},
|
||||
"packages/mobile": {
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/native": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/native": {
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/oauth": {
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/jwt": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/observability": {
|
||||
"name": "@wrnexus/observability",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/playground": {
|
||||
"name": "@wrnexus/playground",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "workspace:*",
|
||||
"@wrnexus/syntax": "workspace:*",
|
||||
@@ -396,18 +396,18 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/pubsub": {
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/pwa": {
|
||||
"name": "@wrnexus/pwa",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.14",
|
||||
"typescript": "^5.9.2",
|
||||
@@ -415,18 +415,18 @@
|
||||
},
|
||||
"packages/queue": {
|
||||
"name": "@wrnexus/queue",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/reactive": {
|
||||
"name": "@wrnexus/reactive",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/realtime": {
|
||||
"name": "@wrnexus/realtime",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
@@ -440,7 +440,7 @@
|
||||
},
|
||||
"packages/router": {
|
||||
"name": "@wrnexus/router",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/compiler": "workspace:*",
|
||||
"@wrnexus/core": "workspace:*",
|
||||
@@ -448,7 +448,7 @@
|
||||
},
|
||||
"packages/rpc": {
|
||||
"name": "@wrnexus/rpc",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
"@wrnexus/core": "workspace:*",
|
||||
@@ -463,14 +463,14 @@
|
||||
},
|
||||
"packages/security": {
|
||||
"name": "@wrnexus/security",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/ssr": {
|
||||
"name": "@wrnexus/ssr",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/security": "workspace:*",
|
||||
@@ -479,11 +479,11 @@
|
||||
},
|
||||
"packages/store": {
|
||||
"name": "@wrnexus/store",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/styles": {
|
||||
"name": "@wrnexus/styles",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
@@ -492,19 +492,19 @@
|
||||
},
|
||||
"packages/syntax": {
|
||||
"name": "@wrnexus/syntax",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/test": {
|
||||
"name": "@wrnexus/test",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/tracking": {
|
||||
"name": "@wrnexus/tracking",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
},
|
||||
"packages/typecheck": {
|
||||
"name": "@wrnexus/typecheck",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/syntax": "workspace:*",
|
||||
"typescript": "^5.5.0",
|
||||
@@ -512,14 +512,14 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@wrnexus/ui",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
},
|
||||
"packages/uploader": {
|
||||
"name": "@wrnexus/uploader",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
@@ -533,7 +533,7 @@
|
||||
},
|
||||
"packages/validation": {
|
||||
"name": "@wrnexus/validation",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
"@wrnexus/plugin": "workspace:*",
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "wrnexus",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wrnexus",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"vscode-languageclient": "^10.1.0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "wrnexus",
|
||||
"displayName": "WRNexus Language Support",
|
||||
"description": "Complete WRNexus v0.8.3 language support for typed imports, props, state, outputs, runtime functions, stores, diagnostics, formatting, navigation, and migration assistance.",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"publisher": "wrnexus",
|
||||
"private": true,
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// WRN editor extension source hash: 6afaf7958331407986761b491ad81d56458dcb162f56369a4cc29f86ff95d981
|
||||
// WRN editor extension source hash: 6165420f37af57e441086efeb6b25528f72e77a39abb2c76ca5bc8748dd90c2f
|
||||
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
||||
"use strict";
|
||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
// WRN editor language server source hash: c4362f7edf117e0c403c9bce9435bc02ae0febd963edb4af9a18d2d74d87eb47
|
||||
// WRN editor language server source hash: 65a5536efbc7c9fee15040559bb48afe78c4686fc5e7341ffc941cc7113c8556
|
||||
// WRN editor language server generator hash: f593a44aaf05495b789ce7a3086bee1eebb951b884d41c0e017bbcfe5f547e72
|
||||
// @bun @bun-cjs
|
||||
(function(exports, require, module, __filename, __dirname) {var __create = Object.create;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wrnexus",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "An SSR-first full-stack web framework with server-rendered reactive components. Bun-first, Node-friendly.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/ai",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/auth",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"description": "Complete authentication, account security, MFA, passkeys, recovery, devices, risk, and audit system for WRNexusJS.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/authz",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/benchmark",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Deterministic benchmark runner and performance regression budgets for WRNexusJS.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cache",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Tag-aware memory and response caching with stale-while-revalidate for WRNexusJS.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/captcha",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"description": "First-class CAPTCHA challenges, providers, verification guards, page gates, and WRNexusJS UI.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/cli",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/compiler",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/content",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Typed Markdown content collections, loaders, indexes, feeds, and preview workflows for WRNexusJS.",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/core",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/db",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/dev-toolbar",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/encryption",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/graphql",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/helpers",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Safe convenience helpers for WrNexus request contexts and common application flows.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/i18n",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/identity",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Enterprise federation, provisioning, machine identity, and privacy governance for WRNexusJS.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/image",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Responsive image planning, secure remote image policies, and performance auditing for WRNexusJS.",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/jwt",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/language-server",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Editor-neutral Language Server Protocol implementation for WRNexus .wrn files.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mcp",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Model Context Protocol server exposing WRNexus application and framework context.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/mobile",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/native",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/oauth",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/observability",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Metrics, Web Vitals collection, request instrumentation, and exporter adapters for WRNexusJS.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/playground",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Secure, shareable WRNexus compiler and UI playground.",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/plugin",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pubsub",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/pwa",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"type": "module",
|
||||
"description": "Service workers, manifests, offline queues, background sync, push, and conflict resolution for WRNexusJS.",
|
||||
"main": "./src/index.ts",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user