feat(ui): build remaining component scaffolds
This commit is contained in:
@@ -24,7 +24,7 @@ import { pathToFileURL } from "node:url";
|
||||
import { compileWireFile } from "@wrnexus/compiler";
|
||||
import { createContext } from "@wrnexus/core";
|
||||
import type { Context } from "@wrnexus/core";
|
||||
import { getReactiveRuntime } from "@wrnexus/csr";
|
||||
import { getComponentControllerRuntime, getReactiveRuntime } from "@wrnexus/csr";
|
||||
import { startServer } from "@wrnexus/dev-server";
|
||||
import {
|
||||
loadAppConfig,
|
||||
@@ -100,6 +100,7 @@ export function mountHtml(html: string): {
|
||||
g.HTMLSelectElement = win.HTMLSelectElement;
|
||||
g.HTMLTextAreaElement = win.HTMLTextAreaElement;
|
||||
(0, eval)(getReactiveRuntime());
|
||||
(0, eval)(getComponentControllerRuntime());
|
||||
(win as { __wrnexusHydrateScopes?: (root: unknown) => void }).__wrnexusHydrateScopes?.(
|
||||
win.document,
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# WRNexus UI component reference
|
||||
|
||||
This reference is generated from the 103 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
|
||||
This reference is generated from the 102 packaged `.wrn` component sources. Props marked required have no default; all others show their runtime default.
|
||||
|
||||
## Advanced-forms
|
||||
|
||||
@@ -22,15 +22,6 @@ Editable autocomplete combobox with local and remote suggestions.
|
||||
- Slots: None
|
||||
- Outputs: `search({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `select({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `change({ value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)`, `clear({ 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({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `close({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `load({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)`
|
||||
|
||||
### CopyMarkup
|
||||
|
||||
Theme-aware, responsive copy markup component.
|
||||
|
||||
- Mount: `data-component="CopyMarkup"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `label: string = "Copy Markup"`, `name: string = ""`, `value: string = ""`, `placeholder: string = ""`, `type: string = "text"`, `min: string = ""`, `max: string = ""`, `step: string = ""`, `disabled: boolean = false`, `required: boolean = false`, `class: string = ""`
|
||||
- Slots: None
|
||||
- Outputs: `copy({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `success({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)`
|
||||
|
||||
### InputNumber
|
||||
|
||||
Theme-aware, responsive input number component.
|
||||
@@ -308,9 +299,9 @@ Present responsive chronological activity, milestones, or workflow status with r
|
||||
Theme-aware, responsive tree view component.
|
||||
|
||||
- Mount: `data-component="TreeView"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Tree View"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Props: `title: string = "Tree View"`, `description: string = ""`, `items: unknown[] = []`, `valueKey: string = "value"`, `labelKey: string = "label"`, `defaultExpanded: unknown[] = []`, `selected: string = ""`, `size: string = "default"`, `color: string = "primary"`, `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)`, `toggle({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `expand({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `collapse({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
- Outputs: `select({ item: object; value: string; sourceEvent?: Event })`, `toggle({ item: object; value: string; expanded: boolean; sourceEvent?: Event })`, `expand({ item: object; value: string; sourceEvent?: Event })`, `collapse({ item: object; value: string; sourceEvent?: Event })`
|
||||
|
||||
## Core
|
||||
|
||||
@@ -505,27 +496,27 @@ Theme-aware, responsive time picker component.
|
||||
Theme-aware, responsive chart component.
|
||||
|
||||
- Mount: `data-component="Chart"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Chart"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Props: `title: string = "Chart"`, `description: string = ""`, `items: unknown[] = []`, `valueKey: string = "value"`, `labelKey: string = "label"`, `height: number = 240`, `showLegend: boolean = true`, `showValues: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `variant: string = "bar"`, `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)`, `dataPointClick({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `legendToggle({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`
|
||||
- Outputs: `select({ item: object; index: number; sourceEvent?: Event })`, `dataPointClick({ item: object; index: number; sourceEvent?: Event })`, `legendToggle({ item: object; index: number; hidden: boolean; sourceEvent?: Event })`
|
||||
|
||||
### Clipboard
|
||||
|
||||
Theme-aware, responsive clipboard component.
|
||||
|
||||
- Mount: `data-component="Clipboard"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Clipboard"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Props: `value: string = ""`, `label: string = "Copy"`, `copiedLabel: string = "Copied"`, `errorLabel: string = "Copy failed"`, `title: string = "Clipboard"`, `description: string = ""`, `code: boolean = true`, `size: string = "default"`, `color: string = "primary"`, `disabled: boolean = false`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `copy({ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])`, `success({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `error({ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)`
|
||||
- Outputs: `copy({ value: string; sourceEvent?: Event })`, `success({ value: string; sourceEvent?: Event })`, `error({ error: Error | string; value: string; sourceEvent?: Event })`
|
||||
|
||||
### Confetti
|
||||
|
||||
Theme-aware, responsive confetti component.
|
||||
|
||||
- Mount: `data-component="Confetti"`
|
||||
- Props: `size: string = "default"`, `color: string = "primary"`, `title: string = "Confetti"`, `description: string = ""`, `items: unknown[] = []`, `variant: string = "default"`, `class: string = ""`
|
||||
- Props: `label: string = "Celebrate"`, `duration: number = 1200`, `count: number = 24`, `autoStart: boolean = false`, `disabled: boolean = false`, `size: string = "default"`, `color: string = "primary"`, `class: string = ""`
|
||||
- Slots: `default`
|
||||
- Outputs: `start({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`, `complete({ sourceEvent?: Event; [key: string]: string | number | boolean | null | object })`
|
||||
- Outputs: `start({ sourceEvent?: Event; duration: number })`, `complete({ sourceEvent?: Event; duration: number })`
|
||||
|
||||
### DataMap
|
||||
|
||||
|
||||
@@ -143,11 +143,6 @@
|
||||
"category": "overlays",
|
||||
"purpose": "Open an accessible keyboard-aware action menu from pointer or keyboard context interactions."
|
||||
},
|
||||
{
|
||||
"name": "CopyMarkup",
|
||||
"category": "advanced-forms",
|
||||
"purpose": "Theme-aware, responsive copy markup component."
|
||||
},
|
||||
{
|
||||
"name": "CustomScrollbar",
|
||||
"category": "layout",
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"generatedFrom": "framework remediation 0.8.7",
|
||||
"removedCount": 5,
|
||||
"removedCount": 6,
|
||||
"replacements": {
|
||||
"AdvancedDatePicker": "DatePicker",
|
||||
"AdvancedRangeSlider": "RangeSlider",
|
||||
"FileUpload": "FileInput",
|
||||
"Toast": "Toaster",
|
||||
"ToastNotifications": "Toaster"
|
||||
"ToastNotifications": "Toaster",
|
||||
"CopyMarkup": "Clipboard"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"generatedFrom": "packages/ui/components/*.wrn",
|
||||
"count": 103,
|
||||
"count": 102,
|
||||
"components": [
|
||||
{
|
||||
"name": "Accordion",
|
||||
@@ -2852,20 +2852,6 @@
|
||||
"category": "integrations",
|
||||
"purpose": "Theme-aware, responsive chart component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
@@ -2888,12 +2874,61 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"name": "valueKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"value\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "labelKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"label\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "240",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showLegend",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "showValues",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"bar\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -2908,15 +2943,15 @@
|
||||
"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"
|
||||
"payloadType": "{ item: object; index: number; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "dataPointClick",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
"payloadType": "{ item: object; index: number; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "legendToggle",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
"payloadType": "{ item: object; index: number; hidden: boolean; sourceEvent?: Event }"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -3276,17 +3311,31 @@
|
||||
"purpose": "Theme-aware, responsive clipboard component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"name": "value",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"default": "\"Copy\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "copiedLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Copied\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "errorLabel",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Copy failed\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
@@ -3304,19 +3353,33 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"type": "unknown[]",
|
||||
"name": "code",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"default": "true",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -3331,15 +3394,15 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "copy",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
"payloadType": "{ value: string; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ value: string; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"payloadType": "{ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string"
|
||||
"payloadType": "{ error: Error | string; value: string; sourceEvent?: Event }"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -4006,6 +4069,41 @@
|
||||
"category": "integrations",
|
||||
"purpose": "Theme-aware, responsive confetti component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Celebrate\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "duration",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "1200",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"default": "24",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "autoStart",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
@@ -4020,34 +4118,6 @@
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Confetti\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "items",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -4062,11 +4132,11 @@
|
||||
"outputs": [
|
||||
{
|
||||
"name": "start",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ sourceEvent?: Event; duration: number }"
|
||||
},
|
||||
{
|
||||
"name": "complete",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ sourceEvent?: Event; duration: number }"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -4303,126 +4373,6 @@
|
||||
],
|
||||
"source": "components/ContextMenu.wrn"
|
||||
},
|
||||
{
|
||||
"name": "CopyMarkup",
|
||||
"mount": "CopyMarkup",
|
||||
"category": "advanced-forms",
|
||||
"purpose": "Theme-aware, responsive copy markup component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "label",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"Copy Markup\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "placeholder",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"text\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "min",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "max",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "step",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "required",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"default": "false",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
}
|
||||
],
|
||||
"slots": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "copy",
|
||||
"payloadType": "{ sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[]"
|
||||
},
|
||||
{
|
||||
"name": "success",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"payloadType": "{ error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
"copy",
|
||||
"success",
|
||||
"error"
|
||||
],
|
||||
"source": "components/CopyMarkup.wrn"
|
||||
},
|
||||
{
|
||||
"name": "CustomScrollbar",
|
||||
"mount": "CustomScrollbar",
|
||||
@@ -13973,20 +13923,6 @@
|
||||
"category": "base",
|
||||
"purpose": "Theme-aware, responsive tree view component.",
|
||||
"props": [
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
@@ -14009,12 +13945,47 @@
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "variant",
|
||||
"name": "valueKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"value\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "labelKey",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"label\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "defaultExpanded",
|
||||
"type": "unknown[]",
|
||||
"required": false,
|
||||
"default": "[]",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "selected",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"default\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"default": "\"primary\"",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "class",
|
||||
"type": "string",
|
||||
@@ -14029,19 +14000,19 @@
|
||||
"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"
|
||||
"payloadType": "{ item: object; value: string; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "toggle",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ item: object; value: string; expanded: boolean; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "expand",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ item: object; value: string; sourceEvent?: Event }"
|
||||
},
|
||||
{
|
||||
"name": "collapse",
|
||||
"payloadType": "{ sourceEvent?: Event; [key: string]: string | number | boolean | null | object }"
|
||||
"payloadType": "{ item: object; value: string; sourceEvent?: Event }"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
|
||||
@@ -1,25 +1,107 @@
|
||||
component Chart {
|
||||
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)
|
||||
dataPointClick(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
legendToggle(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
select(payload: { item: object; index: number; sourceEvent?: Event })
|
||||
dataPointClick(payload: { item: object; index: number; sourceEvent?: Event })
|
||||
legendToggle(payload: { item: object; index: number; hidden: boolean; sourceEvent?: Event })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
title: string = "Chart"
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
valueKey: string = "value"
|
||||
labelKey: string = "label"
|
||||
height: number = 240
|
||||
showLegend: boolean = true
|
||||
showValues: boolean = true
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
variant: string = "bar"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state hiddenIndexes = []
|
||||
|
||||
functions {
|
||||
shared function numericValue(item) { return Math.max(0, Number(item[valueKey]) || 0) }
|
||||
shared function maximumValue() {
|
||||
return Math.max(1, ...items.map(function (item) { return numericValue(item) }))
|
||||
}
|
||||
shared function isHidden(index) { return hiddenIndexes.includes(index) }
|
||||
shared function barWidth() { return items.length ? Math.max(4, 80 / items.length) : 80 }
|
||||
shared function barX(index) { return 10 + index * (80 / Math.max(1, items.length)) }
|
||||
shared function barHeight(item) { return numericValue(item) / maximumValue() * 72 }
|
||||
shared function barY(item) { return 84 - barHeight(item) }
|
||||
|
||||
client function selectPoint(item, index, sourceEvent) {
|
||||
output.dataPointClick({ item: item, index: index, sourceEvent: sourceEvent })
|
||||
output.select({ item: item, index: index, sourceEvent: sourceEvent })
|
||||
}
|
||||
|
||||
client function toggleLegend(item, index, sourceEvent, next) {
|
||||
next = hiddenIndexes.slice()
|
||||
if (next.includes(index)) {
|
||||
next = next.filter(function (value) { return value !== index })
|
||||
} else {
|
||||
next.push(index)
|
||||
}
|
||||
hiddenIndexes = next
|
||||
output.legendToggle({ item: item, index: index, hidden: next.includes(index), sourceEvent: sourceEvent })
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--chart wire-next--variant-{variant} {class}">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
|
||||
<figure {...attrs} data-ui-component="Chart" data-variant='{variant}' class='wire-chart {class}'>
|
||||
{#if title || description}
|
||||
<figcaption class="wire-chart__header">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<span>{description}</span>{/if}
|
||||
</figcaption>
|
||||
{/if}
|
||||
<svg class="wire-chart__plot" viewBox="0 0 100 100" role="img" aria-label='{title}' style='height:{height}px'>
|
||||
<line x1="8" y1="84" x2="96" y2="84" class="wire-chart__axis" />
|
||||
{#each items as item, index}
|
||||
<g data-hidden='{isHidden(index) ? "true" : "false"}'>
|
||||
<rect
|
||||
x='{barX(index)}' y='{barY(item)}' width='{barWidth()}' height='{barHeight(item)}'
|
||||
rx="1.5" class="wire-chart__bar" role="button" tabindex="0"
|
||||
aria-label='{item[labelKey] + ": " + numericValue(item)}'
|
||||
@click='selectPoint(item, index, event)'
|
||||
@keydown='if (event.key === "Enter" || event.key === " ") { event.preventDefault(); selectPoint(item, index, event) }'
|
||||
/>
|
||||
{#if showValues}<text x='{barX(index) + barWidth() / 2}' y='{barY(item) - 3}' class="wire-chart__value">{numericValue(item)}</text>{/if}
|
||||
<text x='{barX(index) + barWidth() / 2}' y="94" class="wire-chart__label">{item[labelKey]}</text>
|
||||
</g>
|
||||
{/each}
|
||||
</svg>
|
||||
{#if showLegend}
|
||||
<div class="wire-chart__legend" aria-label="Chart legend">
|
||||
{#each items as item, index}
|
||||
<button type="button" aria-pressed='{isHidden(index) ? "false" : "true"}' @click='toggleLegend(item, index, event)'>
|
||||
<span aria-hidden="true"></span>{item[labelKey]}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</section>
|
||||
</figure>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-chart { display: grid; gap: 1rem; min-width: 0; margin: 0; padding: 1rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); color: var(--wire-color-text); background: var(--wire-color-surface); box-shadow: var(--wire-shadow-1); }
|
||||
.wire-chart__header { display: grid; gap: 0.2rem; }
|
||||
.wire-chart__header > span { color: var(--wire-color-muted); font-size: 0.8rem; }
|
||||
.wire-chart__plot { display: block; width: 100%; min-height: 12rem; overflow: visible; }
|
||||
.wire-chart__axis { stroke: var(--wire-color-border); stroke-width: 0.5; }
|
||||
.wire-chart__bar { fill: var(--wire-color-primary); cursor: pointer; transition: opacity var(--wire-motion-fast), transform var(--wire-motion-fast); transform-box: fill-box; transform-origin: bottom; }
|
||||
.wire-chart__bar:hover { opacity: 0.82; transform: scaleY(1.02); }
|
||||
.wire-chart__bar:focus-visible { outline: 1.5px solid var(--wire-color-focus); outline-offset: 1px; }
|
||||
.wire-chart__plot g[data-hidden="true"] { opacity: 0.18; pointer-events: none; }
|
||||
.wire-chart__value, .wire-chart__label { fill: var(--wire-color-text); font-size: 3.5px; text-anchor: middle; }
|
||||
.wire-chart__label { fill: var(--wire-color-muted); }
|
||||
.wire-chart__legend { display: flex; flex-wrap: wrap; gap: 0.5rem; }
|
||||
.wire-chart__legend button { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.5rem; border: 1px solid var(--wire-color-border); border-radius: 999px; color: var(--wire-color-text); background: transparent; font: inherit; font-size: 0.75rem; cursor: pointer; }
|
||||
.wire-chart__legend button > span { width: 0.65rem; height: 0.65rem; border-radius: 0.2rem; background: var(--wire-color-primary); }
|
||||
.wire-chart__legend button[aria-pressed="false"] { opacity: 0.5; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,97 @@
|
||||
component Clipboard {
|
||||
outputs {
|
||||
copy(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
success(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
error(payload: { error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)
|
||||
copy(payload: { value: string; sourceEvent?: Event })
|
||||
success(payload: { value: string; sourceEvent?: Event })
|
||||
error(payload: { error: Error | string; value: string; sourceEvent?: Event })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
value: string = ""
|
||||
label: string = "Copy"
|
||||
copiedLabel: string = "Copied"
|
||||
errorLabel: string = "Copy failed"
|
||||
title: string = "Clipboard"
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
code: boolean = true
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
disabled: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state status = "idle"
|
||||
|
||||
functions {
|
||||
client function resetStatus() { status = "idle" }
|
||||
client function copySucceeded() {
|
||||
status = "success"
|
||||
output.success({ value: value })
|
||||
setTimeout(resetStatus, 1600)
|
||||
}
|
||||
client function copyFailed(error) {
|
||||
status = "error"
|
||||
output.error({ error: error, value: value })
|
||||
setTimeout(resetStatus, 1600)
|
||||
}
|
||||
client function copyValue(sourceEvent) {
|
||||
if (disabled || status === "copying") {
|
||||
return
|
||||
}
|
||||
status = "copying"
|
||||
output.copy({ value: value, sourceEvent: sourceEvent })
|
||||
if (!window.navigator.clipboard || !window.navigator.clipboard.writeText) {
|
||||
copyFailed(new Error("Clipboard API is unavailable"))
|
||||
return
|
||||
}
|
||||
return window.navigator.clipboard.writeText(value).then(copySucceeded).catch(copyFailed)
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--clipboard wire-next--variant-{variant} {class}">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
|
||||
<section
|
||||
{...attrs}
|
||||
data-ui-component="Clipboard"
|
||||
data-status='{status}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
class='wire-clipboard {class}'
|
||||
>
|
||||
{#if title}<strong class="wire-clipboard__title">{title}</strong>{/if}
|
||||
{#if description}<p class="wire-clipboard__description">{description}</p>{/if}
|
||||
<div class="wire-clipboard__control">
|
||||
{#if code}
|
||||
<code class="wire-clipboard__value">{value}</code>
|
||||
{:else}
|
||||
<span class="wire-clipboard__value">{value}</span>
|
||||
{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="wire-clipboard__button"
|
||||
disabled='{disabled}'
|
||||
aria-label='{status === "success" ? copiedLabel : status === "error" ? errorLabel : label}'
|
||||
@click='copyValue(event)'
|
||||
>
|
||||
<span class="icon-[lucide--copy] wire-clipboard__icon" data-show='status !== "success"' aria-hidden="true"></span>
|
||||
<span class="icon-[lucide--check] wire-clipboard__icon" data-show='status === "success"' aria-hidden="true"></span>
|
||||
<span>{status === "success" ? copiedLabel : status === "error" ? errorLabel : label}</span>
|
||||
</button>
|
||||
</div>
|
||||
<slot />
|
||||
</section>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-clipboard { display: grid; gap: 0.5rem; color: var(--wire-color-text); }
|
||||
.wire-clipboard__title { font-size: 0.875rem; }
|
||||
.wire-clipboard__description { margin: 0; color: var(--wire-color-muted); font-size: 0.8rem; }
|
||||
.wire-clipboard__control { display: flex; min-width: 0; align-items: stretch; overflow: hidden; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); background: var(--wire-color-surface); box-shadow: var(--wire-shadow-1); }
|
||||
.wire-clipboard__value { min-width: 0; flex: 1; overflow: auto; padding: 0.7rem 0.85rem; color: var(--wire-color-text); background: var(--wire-color-surface-2); font: inherit; font-family: var(--wire-font-mono, ui-monospace, monospace); white-space: nowrap; }
|
||||
.wire-clipboard__button { display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.6rem 0.85rem; border: 0; border-left: 1px solid var(--wire-color-border); color: var(--wire-color-primary); background: transparent; font: inherit; font-size: 0.8rem; font-weight: 700; cursor: pointer; }
|
||||
.wire-clipboard__button:hover { background: var(--wire-color-primary-soft); }
|
||||
.wire-clipboard__button:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: -3px; }
|
||||
.wire-clipboard__button:disabled { cursor: not-allowed; opacity: 0.55; }
|
||||
.wire-clipboard[data-status="success"] .wire-clipboard__button { color: var(--wire-color-success); }
|
||||
.wire-clipboard[data-status="error"] .wire-clipboard__button { color: var(--wire-color-danger); }
|
||||
.wire-clipboard__icon { width: 1rem; height: 1rem; flex: none; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,72 @@
|
||||
component Confetti {
|
||||
outputs {
|
||||
start(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
complete(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
start(payload: { sourceEvent?: Event; duration: number })
|
||||
complete(payload: { sourceEvent?: Event; duration: number })
|
||||
}
|
||||
|
||||
props {
|
||||
label: string = "Celebrate"
|
||||
duration: number = 1200
|
||||
count: number = 24
|
||||
autoStart: boolean = false
|
||||
disabled: boolean = false
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
title: string = "Confetti"
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state running = false
|
||||
|
||||
functions {
|
||||
shared function pieces() { return Array.from({ length: Math.max(1, Math.min(60, count)) }) }
|
||||
shared function pieceStyle(index) {
|
||||
return "--wire-confetti-index:" + index + ";--wire-confetti-x:" + ((index * 37) % 100) + ";--wire-confetti-delay:" + ((index * 29) % 240) + "ms;"
|
||||
}
|
||||
client function finish(sourceEvent) {
|
||||
running = false
|
||||
output.complete({ sourceEvent: sourceEvent, duration: duration })
|
||||
}
|
||||
client function start(sourceEvent) {
|
||||
if (disabled || running) {
|
||||
return
|
||||
}
|
||||
running = true
|
||||
output.start({ sourceEvent: sourceEvent, duration: duration })
|
||||
setTimeout(finish, duration, sourceEvent)
|
||||
}
|
||||
}
|
||||
|
||||
lifecycle {
|
||||
mount { if (autoStart) { start(null) } }
|
||||
}
|
||||
|
||||
view {
|
||||
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--confetti wire-next--variant-{variant} {class}">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
|
||||
<div {...attrs} data-ui-component="Confetti" data-running='{running ? "true" : "false"}' class='wire-confetti {class}' style='--wire-confetti-duration:{duration}ms'>
|
||||
<button type="button" class="wire-confetti__trigger" disabled='{disabled}' @click='start(event)'>
|
||||
<span class="icon-[lucide--party-popper]" aria-hidden="true"></span><span>{label}</span>
|
||||
</button>
|
||||
<div class="wire-confetti__burst" aria-hidden="true">
|
||||
{#each pieces() as piece, index}<i style='{pieceStyle(index)}'></i>{/each}
|
||||
</div>
|
||||
<slot />
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-confetti { position: relative; display: inline-flex; }
|
||||
.wire-confetti__trigger { display: inline-flex; min-height: 2.5rem; align-items: center; gap: 0.5rem; padding: 0.55rem 0.85rem; border: 1px solid var(--wire-color-primary); border-radius: var(--wire-radius); color: var(--wire-color-on-primary); background: var(--wire-color-primary); font: inherit; font-weight: 700; cursor: pointer; }
|
||||
.wire-confetti__trigger > span:first-child { width: 1rem; height: 1rem; }
|
||||
.wire-confetti__trigger:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: 2px; }
|
||||
.wire-confetti__trigger:disabled { cursor: not-allowed; opacity: 0.55; }
|
||||
.wire-confetti__burst { position: absolute; inset: 50% 0 auto; height: 0; pointer-events: none; }
|
||||
.wire-confetti__burst > i { position: absolute; left: calc(var(--wire-confetti-x) * 1%); width: 0.45rem; height: 0.7rem; border-radius: 0.1rem; opacity: 0; background: hsl(calc(var(--wire-confetti-index) * 47deg) 80% 55%); transform: translate(-50%, 0) rotate(calc(var(--wire-confetti-index) * 23deg)); }
|
||||
.wire-confetti[data-running="true"] .wire-confetti__burst > i { animation: wire-confetti-burst var(--wire-confetti-duration) cubic-bezier(0.2, 0.7, 0.2, 1) var(--wire-confetti-delay) both; }
|
||||
@keyframes wire-confetti-burst {
|
||||
0% { opacity: 1; transform: translate(-50%, 0) rotate(0); }
|
||||
100% { opacity: 0; transform: translate(calc(-50% + (var(--wire-confetti-x) - 50) * 0.9px), 9rem) rotate(540deg); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.wire-confetti[data-running="true"] .wire-confetti__burst > i { animation-duration: 1ms; animation-delay: 0ms; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
component CopyMarkup {
|
||||
outputs {
|
||||
copy(payload: { sourceEvent?: Event; value: string | number | boolean | null | object; [key: string]: string | number | boolean | null | object } | string | number | boolean | null | object[])
|
||||
success(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
error(payload: { error: Error | string; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | Error | string)
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
label: string = "Copy Markup"
|
||||
name: string = ""
|
||||
value: string = ""
|
||||
placeholder: string = ""
|
||||
type: string = "text"
|
||||
min: string = ""
|
||||
max: string = ""
|
||||
step: string = ""
|
||||
disabled: boolean = false
|
||||
required: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
view {
|
||||
<label class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--copy-markup {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,103 @@
|
||||
component TreeView {
|
||||
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)
|
||||
toggle(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
expand(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
collapse(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
|
||||
select(payload: { item: object; value: string; sourceEvent?: Event })
|
||||
toggle(payload: { item: object; value: string; expanded: boolean; sourceEvent?: Event })
|
||||
expand(payload: { item: object; value: string; sourceEvent?: Event })
|
||||
collapse(payload: { item: object; value: string; sourceEvent?: Event })
|
||||
}
|
||||
|
||||
props {
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
title: string = "Tree View"
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
valueKey: string = "value"
|
||||
labelKey: string = "label"
|
||||
defaultExpanded: unknown[] = []
|
||||
selected: string = ""
|
||||
size: string = "default"
|
||||
color: string = "primary"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
state expandedValues = defaultExpanded
|
||||
state selectedValue = selected
|
||||
|
||||
functions {
|
||||
shared function nodeValue(item, index, parent) { return String(item[valueKey] || item.id || parent + index) }
|
||||
shared function isExpanded(value) { return expandedValues.includes(value) }
|
||||
|
||||
client function toggleNode(item, value, sourceEvent, next, expanded) {
|
||||
next = expandedValues.slice()
|
||||
expanded = !next.includes(value)
|
||||
if (expanded) {
|
||||
next.push(value)
|
||||
} else {
|
||||
next = next.filter(function (entry) { return entry !== value })
|
||||
}
|
||||
expandedValues = next
|
||||
output.toggle({ item: item, value: value, expanded: expanded, sourceEvent: sourceEvent })
|
||||
if (expanded) {
|
||||
output.expand({ item: item, value: value, sourceEvent: sourceEvent })
|
||||
} else {
|
||||
output.collapse({ item: item, value: value, sourceEvent: sourceEvent })
|
||||
}
|
||||
}
|
||||
|
||||
client function selectNode(item, value, sourceEvent) {
|
||||
selectedValue = value
|
||||
output.select({ item: item, value: value, sourceEvent: sourceEvent })
|
||||
}
|
||||
}
|
||||
|
||||
view {
|
||||
<section class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--tree-view wire-next--variant-{variant} {class}">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
|
||||
<section {...attrs} data-ui-component="TreeView" class='wire-tree-view {class}'>
|
||||
{#if title}<strong class="wire-tree-view__title">{title}</strong>{/if}
|
||||
{#if description}<p class="wire-tree-view__description">{description}</p>{/if}
|
||||
<div class="wire-tree-view__tree" role="tree" aria-label='{title}' data-wrn-roving="vertical">
|
||||
{#each items as item, index}
|
||||
<div class="wire-tree-view__branch" role="none">
|
||||
<div class="wire-tree-view__row" role="treeitem" aria-level="1" aria-expanded='{item.children && item.children.length ? isExpanded(nodeValue(item, index, "root-")) : ""}' aria-selected='{selectedValue === nodeValue(item, index, "root-") ? "true" : "false"}'>
|
||||
{#if item.children && item.children.length}
|
||||
<button type="button" class="wire-tree-view__toggle" aria-label='{isExpanded(nodeValue(item, index, "root-")) ? "Collapse " + item[labelKey] : "Expand " + item[labelKey]}' @click='toggleNode(item, nodeValue(item, index, "root-"), event)'><span class="icon-[lucide--chevron-right]" aria-hidden="true"></span></button>
|
||||
{:else}<span class="wire-tree-view__toggle" aria-hidden="true"></span>{/if}
|
||||
<button type="button" class="wire-tree-view__node" data-wrn-roving-item @click='selectNode(item, nodeValue(item, index, "root-"), event)'>
|
||||
{#if item.icon}<span class='{item.icon}' aria-hidden="true"></span>{/if}<span>{item[labelKey]}</span>
|
||||
</button>
|
||||
</div>
|
||||
{#if item.children && item.children.length}
|
||||
<div class="wire-tree-view__group" role="group" data-show='isExpanded(nodeValue(item, index, "root-"))'>
|
||||
{#each item.children as child, childIndex}
|
||||
<div class="wire-tree-view__row" role="treeitem" aria-level="2" aria-selected='{selectedValue === nodeValue(child, childIndex, nodeValue(item, index, "root-") + "-") ? "true" : "false"}'>
|
||||
<span class="wire-tree-view__toggle" aria-hidden="true"></span>
|
||||
<button type="button" class="wire-tree-view__node" data-wrn-roving-item @click='selectNode(child, nodeValue(child, childIndex, nodeValue(item, index, "root-") + "-"), event)'>
|
||||
{#if child.icon}<span class='{child.icon}' aria-hidden="true"></span>{/if}<span>{child[labelKey]}</span>
|
||||
</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:empty}<p class="wire-tree-view__empty">No tree items available.</p>{/each}
|
||||
</div>
|
||||
<slot />
|
||||
</section>
|
||||
}
|
||||
|
||||
style {
|
||||
.wire-tree-view { display: grid; gap: 0.5rem; color: var(--wire-color-text); }
|
||||
.wire-tree-view__title { font-size: 0.95rem; }
|
||||
.wire-tree-view__description, .wire-tree-view__empty { margin: 0; color: var(--wire-color-muted); font-size: 0.8rem; }
|
||||
.wire-tree-view__tree { display: grid; gap: 0.15rem; padding: 0.4rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius); background: var(--wire-color-surface); }
|
||||
.wire-tree-view__row { display: flex; min-width: 0; align-items: center; gap: 0.2rem; border-radius: var(--wire-radius-sm); }
|
||||
.wire-tree-view__row[aria-selected="true"] { background: var(--wire-color-primary-soft); }
|
||||
.wire-tree-view__toggle { display: inline-grid; width: 1.8rem; height: 1.8rem; flex: none; padding: 0; place-items: center; border: 0; border-radius: 0.35rem; color: var(--wire-color-muted); background: transparent; }
|
||||
button.wire-tree-view__toggle { cursor: pointer; }
|
||||
.wire-tree-view__toggle > span { width: 0.9rem; height: 0.9rem; transition: transform var(--wire-motion-fast); }
|
||||
.wire-tree-view__row[aria-expanded="true"] > .wire-tree-view__toggle > span { transform: rotate(90deg); }
|
||||
.wire-tree-view__node { display: flex; min-width: 0; flex: 1; align-items: center; gap: 0.45rem; padding: 0.45rem 0.5rem; border: 0; border-radius: var(--wire-radius-sm); color: inherit; background: transparent; font: inherit; text-align: left; cursor: pointer; }
|
||||
.wire-tree-view__node:hover, button.wire-tree-view__toggle:hover { background: var(--wire-color-surface-2); }
|
||||
.wire-tree-view__node:focus-visible, button.wire-tree-view__toggle:focus-visible { outline: 2px solid var(--wire-color-focus); outline-offset: -2px; }
|
||||
.wire-tree-view__node > [class*="icon-"] { width: 1rem; height: 1rem; flex: none; color: var(--wire-color-primary); }
|
||||
.wire-tree-view__group { display: grid; margin-left: 1.8rem; padding-left: 0.45rem; border-left: 1px solid var(--wire-color-border); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ test("global UI CSS stays below its migration ratchet", () => {
|
||||
expect(gzipSync(new TextEncoder().encode(uiCss())).length).toBeLessThanOrEqual(12_672);
|
||||
});
|
||||
|
||||
test("only the five scheduled component builds remain without local styles", () => {
|
||||
test("every bundled component carries local styles", () => {
|
||||
const unstyled = uiComponentNames().filter(
|
||||
(name) => !/^\s{2,4}style \{/m.test(readFileSync(uiComponentPath(name), "utf8")),
|
||||
);
|
||||
expect(unstyled.sort()).toEqual(["Chart", "Clipboard", "Confetti", "CopyMarkup", "TreeView"]);
|
||||
expect(unstyled).toEqual([]);
|
||||
});
|
||||
|
||||
test("generated component reference documents every bundled component and its props", () => {
|
||||
@@ -45,7 +45,7 @@ test("generated component reference documents every bundled component and its pr
|
||||
readFileSync(join(uiComponentsDir(), "..", "component-catalog.json"), "utf8"),
|
||||
) as { components: Array<{ name: string }> };
|
||||
expect(reference.count).toBe(uiComponentNames().length);
|
||||
expect(reference.count).toBe(103);
|
||||
expect(reference.count).toBe(102);
|
||||
expect(reference.components.map((component) => component.name)).toEqual(
|
||||
expect.arrayContaining(uiComponentNames()),
|
||||
);
|
||||
@@ -146,7 +146,7 @@ test("removed duplicate aliases have valid canonical migration targets", () => {
|
||||
const names = new Set(uiComponentNames());
|
||||
|
||||
expect(Object.keys(migration.replacements)).toHaveLength(migration.removedCount);
|
||||
expect(migration.removedCount).toBe(5);
|
||||
expect(migration.removedCount).toBe(6);
|
||||
for (const [removed, replacement] of Object.entries(migration.replacements)) {
|
||||
expect(names.has(removed)).toBe(false);
|
||||
expect(names.has(replacement)).toBe(true);
|
||||
@@ -3200,7 +3200,7 @@ test("no component gains an output that nothing ever emits", () => {
|
||||
* A ceiling, not a target. It only ever moves down: rebuilding one of these
|
||||
* components should tighten it.
|
||||
*/
|
||||
expect(offenders.length).toBeLessThanOrEqual(7);
|
||||
expect(offenders.length).toBe(0);
|
||||
expect(offenders).not.toContain("LayoutSplitter.sizeChange");
|
||||
expect(offenders).not.toContain("CustomScrollbar.scroll");
|
||||
});
|
||||
@@ -3224,6 +3224,64 @@ test("list selects items without hrefs as well as navigation items", async () =>
|
||||
expect(events).toEqual([{ item: { label: "Run report" }, index: 0 }]);
|
||||
});
|
||||
|
||||
test("chart renders accessible SVG bars and emits point and legend outputs", async () => {
|
||||
const source = readFileSync(uiComponentPath("Chart"), "utf8");
|
||||
const dom = mountHtml(
|
||||
await renderComponent(source, { items: [{ label: "Alpha", value: 12 }, { label: "Beta", value: 6 }] }),
|
||||
);
|
||||
const root = dom.querySelector('[data-ui-component="Chart"]') as HTMLElement;
|
||||
const events: string[] = [];
|
||||
for (const name of ["dataPointClick", "select", "legendToggle"]) {
|
||||
root.addEventListener(name, () => events.push(name));
|
||||
}
|
||||
expect(root.querySelectorAll("svg rect")).toHaveLength(2);
|
||||
(root.querySelector("svg rect") as SVGElement).dispatchEvent(
|
||||
new (dom.window as { Event: typeof Event }).Event("click", { bubbles: true }),
|
||||
);
|
||||
(root.querySelector(".wire-chart__legend button") as HTMLButtonElement).click();
|
||||
expect(events).toEqual(["dataPointClick", "select", "legendToggle"]);
|
||||
});
|
||||
|
||||
test("tree view expands branches, selects nodes, and emits public outputs", async () => {
|
||||
const source = readFileSync(uiComponentPath("TreeView"), "utf8");
|
||||
const dom = mountHtml(
|
||||
await renderComponent(source, {
|
||||
items: [{ value: "docs", label: "Docs", children: [{ value: "guide", label: "Guide" }] }],
|
||||
}),
|
||||
);
|
||||
const root = dom.querySelector('[data-ui-component="TreeView"]') as HTMLElement;
|
||||
const events: string[] = [];
|
||||
for (const name of ["toggle", "expand", "select"]) root.addEventListener(name, () => events.push(name));
|
||||
(root.querySelector(".wire-tree-view__toggle") as HTMLButtonElement).click();
|
||||
expect(root.querySelector(".wire-tree-view__group")?.getAttribute("data-show")).toBe("true");
|
||||
(root.querySelector('[aria-level="2"] .wire-tree-view__node') as HTMLButtonElement).click();
|
||||
expect(events).toEqual(["toggle", "expand", "select"]);
|
||||
});
|
||||
|
||||
test("confetti completes deferred bursts and clipboard reports successful copies", async () => {
|
||||
const confetti = mountHtml(await renderComponent(readFileSync(uiComponentPath("Confetti"), "utf8"), { duration: 5, count: 4 }));
|
||||
const confettiRoot = confetti.querySelector('[data-ui-component="Confetti"]') as HTMLElement;
|
||||
const confettiEvents: string[] = [];
|
||||
confettiRoot.addEventListener("start", () => confettiEvents.push("start"));
|
||||
confettiRoot.addEventListener("complete", () => confettiEvents.push("complete"));
|
||||
(confettiRoot.querySelector("button") as HTMLButtonElement).click();
|
||||
await new Promise((resolve) => setTimeout(resolve, 15));
|
||||
expect(confettiEvents).toEqual(["start", "complete"]);
|
||||
|
||||
const clipboard = mountHtml(await renderComponent(readFileSync(uiComponentPath("Clipboard"), "utf8"), { value: "npm add wrnexus" }));
|
||||
Object.defineProperty((clipboard.window as { navigator: object }).navigator, "clipboard", {
|
||||
configurable: true,
|
||||
value: { writeText: () => Promise.resolve() },
|
||||
});
|
||||
const clipboardRoot = clipboard.querySelector('[data-ui-component="Clipboard"]') as HTMLElement;
|
||||
const clipboardEvents: string[] = [];
|
||||
for (const name of ["copy", "success"]) clipboardRoot.addEventListener(name, () => clipboardEvents.push(name));
|
||||
(clipboardRoot.querySelector("button") as HTMLButtonElement).click();
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(clipboardEvents).toEqual(["copy", "success"]);
|
||||
expect(clipboardRoot.dataset.status).toBe("success");
|
||||
});
|
||||
|
||||
test("a declared output is never emitted as a hand-built CustomEvent", () => {
|
||||
/*
|
||||
* The failure this prevents is silent in both directions: the component
|
||||
|
||||
Reference in New Issue
Block a user