From 25b837f49356178a3fc513b4b692cb82e91cd034 Mon Sep 17 00:00:00 2001 From: HerbertJulio Date: Thu, 2 Jul 2026 10:29:19 -0300 Subject: [PATCH 01/67] [NO-ISSUE] feat(webkit): add catalog.json manifest + generator for the adoption toolkit --- packages/webkit/.releaserc | 2 +- packages/webkit/catalog.json | 5839 +++++++++++++++++++++ packages/webkit/package.json | 5 + packages/webkit/scripts/build-catalog.mjs | 225 + 4 files changed, 6070 insertions(+), 1 deletion(-) create mode 100644 packages/webkit/catalog.json create mode 100644 packages/webkit/scripts/build-catalog.mjs diff --git a/packages/webkit/.releaserc b/packages/webkit/.releaserc index fcd4267c6..b44f2c5ff 100644 --- a/packages/webkit/.releaserc +++ b/packages/webkit/.releaserc @@ -51,7 +51,7 @@ [ "@semantic-release/exec", { - "prepareCmd": "vue-tsc --declaration --emitDeclarationOnly", + "prepareCmd": "node scripts/build-catalog.mjs && vue-tsc --declaration --emitDeclarationOnly", "publishCmd": "npm publish --provenance --access public" } ], diff --git a/packages/webkit/catalog.json b/packages/webkit/catalog.json new file mode 100644 index 000000000..06d3ce0fd --- /dev/null +++ b/packages/webkit/catalog.json @@ -0,0 +1,5839 @@ +{ + "package": "@aziontech/webkit", + "webkitVersion": "0.0.0", + "deniedPrefixes": [ + "@aziontech/webkit/src/" + ], + "tokenRules": [ + { + "id": "hex-color", + "pattern": "#[0-9a-fA-F]{3,8}\\b", + "flags": "g", + "message": "Hex color hardcoded. Use semantic tokens (var(--primary), var(--bg-surface), var(--text-default), ...)." + }, + { + "id": "rgb-hsl", + "pattern": "\\b(rgba?|hsla?)\\s*\\(", + "flags": "g", + "message": "RGB/HSL hardcoded. Use semantic tokens via var(--*)." + }, + { + "id": "tailwind-palette", + "pattern": "\\b(?:bg|text|border|ring|outline|fill|stroke|divide|placeholder|caret|accent)-(?:gray|slate|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\\d{2,3}\\b", + "flags": "g", + "message": "Tailwind palette color. Use semantic webkit tokens (var(--primary), var(--text-default), var(--bg-surface), ...)." + }, + { + "id": "typography-raw-size", + "pattern": "\\btext-(?:xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)\\b(?!-)", + "flags": "g", + "message": "Raw Tailwind text size. Use a generated typography class (text-heading-md, text-body-sm, text-button-lg, ...)." + }, + { + "id": "primevue-color", + "pattern": "(?`)." + }, + { + "name": "target", + "type": "'_blank' | '_self'", + "default": "'_self'", + "required": "no", + "doc": "Link target when `href` is set." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "—" + } + ] + }, + "button-highlight": { + "import": "@aziontech/webkit/button-highlight", + "target": "./src/components/actions/button-highlight/button-highlight.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/button-highlight", + "category": "actions", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "label", + "type": "string", + "default": "—", + "required": "yes", + "doc": "Visible label text. Required — use `IconButton` for icon-only controls." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'large'", + "required": "no", + "doc": "Size token; affects height, padding, and typography." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "loading", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Shows loading state and disables activation." + }, + { + "name": "icon", + "type": "string", + "default": "''", + "required": "no", + "doc": "PrimeIcons class for the leading icon." + }, + { + "name": "href", + "type": "string", + "default": "''", + "required": "no", + "doc": "When set, renders as an anchor link." + }, + { + "name": "target", + "type": "'_blank' | '_self'", + "default": "'_self'", + "required": "no", + "doc": "Link target when `href` is set." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "Fires on activation; suppressed when `disabled` or `loading`." + } + ] + }, + "mini-button": { + "import": "@aziontech/webkit/mini-button", + "target": "./src/components/actions/mini-button/mini-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/mini-button", + "category": "actions", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "label", + "type": "string", + "default": "'Learn More'", + "required": "no", + "doc": "Visible label rendered inside the control." + }, + { + "name": "size", + "type": "'large' | 'medium'", + "default": "'large'", + "required": "no", + "doc": "Size token; affects height, gap, and typography." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "showIcon", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, renders the trailing icon." + }, + { + "name": "icon", + "type": "string", + "default": "'pi pi-external-link'", + "required": "no", + "doc": "PrimeIcons class for the trailing icon." + }, + { + "name": "href", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "Destination URL for the anchor." + }, + { + "name": "target", + "type": "'_blank' | '_self'", + "default": "'_self'", + "required": "no", + "doc": "Link target when navigating." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "Fires on activation; suppressed when `disabled`." + } + ] + }, + "copy-button": { + "import": "@aziontech/webkit/copy-button", + "target": "./src/components/actions/copy-button/copy-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/copy-button", + "category": "actions", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "value", + "type": "string", + "default": "—", + "required": "yes", + "doc": "Text copied to the clipboard on activation." + }, + { + "name": "ariaLabel", + "type": "string", + "default": "'Copy'", + "required": "no", + "doc": "Accessible name while idle." + }, + { + "name": "copiedLabel", + "type": "string", + "default": "'Copied'", + "required": "no", + "doc": "Accessible name while the copied state is shown." + }, + { + "name": "kind", + "type": "'primary' | 'secondary' | 'outlined' | 'transparent' | 'danger'", + "default": "'transparent'", + "required": "no", + "doc": "Visual variant forwarded to `IconButton`." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'small'", + "required": "no", + "doc": "Size token forwarded to `IconButton`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + } + ], + "events": [ + { + "name": "copy", + "payload": "string", + "notes": "Emitted after a successful clipboard write with the copied value." + } + ] + }, + "segmented-button": { + "import": "@aziontech/webkit/segmented-button", + "target": "./src/components/actions/segmented-button/segmented-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/segmented-button", + "category": "actions" + }, + "icon-button": { + "import": "@aziontech/webkit/icon-button", + "target": "./src/components/actions/icon-button/icon-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/icon-button", + "category": "actions", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "icon", + "type": "string", + "default": "—", + "required": "yes", + "doc": "PrimeIcons class for the leading/trailing icon." + }, + { + "name": "ariaLabel", + "type": "string", + "default": "—", + "required": "yes", + "doc": "Accessible name for icon-only controls." + }, + { + "name": "kind", + "type": "'primary' | 'secondary' | 'outlined' | 'transparent' | 'danger'", + "default": "'primary'", + "required": "no", + "doc": "Visual variant." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'large'", + "required": "no", + "doc": "Size token; affects height, padding, and typography." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "loading", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Shows loading state and disables activation." + }, + { + "name": "href", + "type": "string", + "default": "''", + "required": "no", + "doc": "When set, renders as a link (``)." + }, + { + "name": "target", + "type": "'_blank' | '_self'", + "default": "'_self'", + "required": "no", + "doc": "Link target when `href` is set." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "—" + } + ] + }, + "tag": { + "import": "@aziontech/webkit/tag", + "target": "./src/components/tag/tag.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/tag", + "category": "content", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "value", + "type": "string", + "default": "'undefined'", + "required": "no", + "doc": "Fallback text when the default slot is empty." + }, + { + "name": "severity", + "type": "'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'accent' | 'contrast'", + "default": "'primary'", + "required": "no", + "doc": "Color style for the tag surface and label; `contrast` uses `var(--bg-contrast)` / `var(--text-contrast)`." + }, + { + "name": "size", + "type": "'small' | 'medium'", + "default": "'medium'", + "required": "no", + "doc": "Size token; `medium` is 24px tall, `small` is 20px." + }, + { + "name": "rounded", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Pill shape when true." + }, + { + "name": "icon", + "type": "string", + "default": "'undefined'", + "required": "no", + "doc": "PrimeIcons class for the leading icon." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "avatar": { + "import": "@aziontech/webkit/avatar", + "target": "./src/components/avatar/avatar.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/avatar", + "category": "content", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "src", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Image URL. When set, renders a photo and takes precedence over label and icon." + }, + { + "name": "alt", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Accessible description for the image. Use when `src` is set." + }, + { + "name": "label", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Initials shown when no image (normalized to two uppercase characters)." + }, + { + "name": "icon", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Icon class for the fallback when no image or label (PrimeIcons class)." + }, + { + "name": "kind", + "type": "AvatarKind", + "default": "undefined", + "required": "no", + "doc": "Visual shape of the avatar container." + }, + { + "name": "size", + "type": "AvatarSize", + "default": "undefined", + "required": "no", + "doc": "Size preset matching Figma sm / md / lg (24 / 32 / 48 px)." + } + ] + }, + "badge": { + "import": "@aziontech/webkit/badge", + "target": "./src/components/content/badge/badge.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/badge", + "category": "content", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "value", + "type": "string", + "default": "''", + "required": "no", + "doc": "Fallback text when the default slot is empty." + }, + { + "name": "severity", + "type": "'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'default'", + "default": "'primary'", + "required": "no", + "doc": "Color style for the badge surface and label; `default` uses the neutral `var(--bg-surface)` / `var(--text-default)` surface." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; `small` is 20px tall, `medium` is 24px, `large` is 30px." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "card-box": { + "import": "@aziontech/webkit/card-box", + "target": "./src/components/content/card-box/card-box.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/card-box", + "category": "content", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "title", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Heading rendered in the header when the `header` slot is empty." + } + ], + "slots": [ + { + "name": "header", + "notes": "—" + }, + { + "name": "content", + "notes": "—" + }, + { + "name": "footer", + "notes": "—" + }, + { + "name": "header-action", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + } + ] + }, + "card-pricing": { + "import": "@aziontech/webkit/card-pricing", + "target": "./src/components/content/card-pricing/card-pricing.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/card-pricing", + "category": "content", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "planTitle", + "type": "string", + "default": "'Pro'", + "required": "no", + "doc": "plan Title." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "description." + }, + { + "name": "pricingDetails", + "type": "string", + "default": "''", + "required": "no", + "doc": "pricing Details." + }, + { + "name": "showPricingDetails", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "show Pricing Details." + }, + { + "name": "showTag", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "show Tag." + }, + { + "name": "tagLabel", + "type": "string", + "default": "'Popular'", + "required": "no", + "doc": "tag Label." + }, + { + "name": "slotPosition", + "type": "'bottom' | 'middle'", + "default": "'bottom'", + "required": "no", + "doc": "slot Position." + }, + { + "name": "cardStyle", + "type": "'contained' | 'transparent'", + "default": "'contained'", + "required": "no", + "doc": "card Style." + }, + { + "name": "value", + "type": "string", + "default": "'20'", + "required": "no", + "doc": "value." + }, + { + "name": "prefix", + "type": "string", + "default": "'$'", + "required": "no", + "doc": "prefix." + }, + { + "name": "suffix", + "type": "string", + "default": "'per month'", + "required": "no", + "doc": "suffix." + }, + { + "name": "showPrefix", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "show Prefix." + }, + { + "name": "showSuffix", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "show Suffix." + }, + { + "name": "actionLabel", + "type": "string", + "default": "'Label'", + "required": "no", + "doc": "action Label." + } + ], + "slots": [ + { + "name": "actions", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + } + ] + }, + "item": { + "import": "@aziontech/webkit/item", + "target": "./src/components/content/item/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/item-root", + "subcomponents": [ + "@aziontech/webkit/item-group", + "@aziontech/webkit/item-separator", + "@aziontech/webkit/item-media", + "@aziontech/webkit/item-content", + "@aziontech/webkit/item-title", + "@aziontech/webkit/item-description", + "@aziontech/webkit/item-actions", + "@aziontech/webkit/item-header", + "@aziontech/webkit/item-footer" + ], + "category": "content", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "kind", + "type": "'default' | 'outline' | 'muted'", + "default": "'default'", + "required": "no", + "doc": "Item root surface variant." + }, + { + "name": "size", + "type": "'small' | 'medium'", + "default": "'medium'", + "required": "no", + "doc": "Item root density (padding and gap)." + }, + { + "name": "asChild", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Merge row layout and interactive-state classes onto the single default-slot child (e.g. anchor)." + }, + { + "name": "mediaKind", + "type": "'default' | 'icon' | 'image'", + "default": "'default'", + "required": "no", + "doc": "ItemMedia region variant (icon frame, image frame)." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "item-root": { + "import": "@aziontech/webkit/item-root", + "target": "./src/components/content/item/item.vue", + "kind": "root", + "rootOf": "item", + "treeShakeableImport": "@aziontech/webkit/item-root", + "category": "content", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "kind", + "type": "'default' | 'outline' | 'muted'", + "default": "'default'", + "required": "no", + "doc": "Item root surface variant." + }, + { + "name": "size", + "type": "'small' | 'medium'", + "default": "'medium'", + "required": "no", + "doc": "Item root density (padding and gap)." + }, + { + "name": "asChild", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Merge row layout and interactive-state classes onto the single default-slot child (e.g. anchor)." + }, + { + "name": "mediaKind", + "type": "'default' | 'icon' | 'image'", + "default": "'default'", + "required": "no", + "doc": "ItemMedia region variant (icon frame, image frame)." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "item-group": { + "import": "@aziontech/webkit/item-group", + "target": "./src/components/content/item/item-group.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-group" + }, + "item-separator": { + "import": "@aziontech/webkit/item-separator", + "target": "./src/components/content/item/item-separator.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-separator" + }, + "item-media": { + "import": "@aziontech/webkit/item-media", + "target": "./src/components/content/item/item-media.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-media" + }, + "item-content": { + "import": "@aziontech/webkit/item-content", + "target": "./src/components/content/item/item-content.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-content" + }, + "item-title": { + "import": "@aziontech/webkit/item-title", + "target": "./src/components/content/item/item-title.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-title" + }, + "item-description": { + "import": "@aziontech/webkit/item-description", + "target": "./src/components/content/item/item-description.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-description" + }, + "item-actions": { + "import": "@aziontech/webkit/item-actions", + "target": "./src/components/content/item/item-actions.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-actions" + }, + "item-header": { + "import": "@aziontech/webkit/item-header", + "target": "./src/components/content/item/item-header.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-header" + }, + "item-footer": { + "import": "@aziontech/webkit/item-footer", + "target": "./src/components/content/item/item-footer.vue", + "kind": "subcomponent", + "parent": "item", + "treeShakeableImport": "@aziontech/webkit/item-footer" + }, + "currency": { + "import": "@aziontech/webkit/currency", + "target": "./src/components/content/currency/currency.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/currency", + "category": "content", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "value", + "type": "string", + "default": "''", + "required": "no", + "doc": "value." + }, + { + "name": "prefix", + "type": "string", + "default": "'$'", + "required": "no", + "doc": "prefix." + }, + { + "name": "suffix", + "type": "string", + "default": "''", + "required": "no", + "doc": "suffix." + }, + { + "name": "size", + "type": "'small' | 'large'", + "default": "'small'", + "required": "no", + "doc": "Size token; affects height, padding, and typography." + } + ] + }, + "log-view": { + "import": "@aziontech/webkit/log-view", + "target": "./src/components/code/log-view/log-view.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/log-view", + "category": "code", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "lines", + "type": "LogViewLine[]", + "default": "[]", + "required": "no", + "doc": "Log entries; filtered in LogViewContent by search and warnings-only." + }, + { + "name": "searchPlaceholder", + "type": "string", + "default": "'Find in Logs'", + "required": "no", + "doc": "Placeholder for the default header search field." + }, + { + "name": "showCopy", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Shows the copy-to-clipboard control in LogViewHeader." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables toolbar controls in LogViewHeader." + } + ], + "events": [ + { + "name": "copy", + "payload": "string", + "notes": "Fires after a successful copy; payload is the copied plain text." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "log-view-content": { + "import": "@aziontech/webkit/log-view-content", + "target": "./src/components/code/log-view/log-view-content.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/log-view-content", + "category": "code" + }, + "log-view-footer": { + "import": "@aziontech/webkit/log-view-footer", + "target": "./src/components/code/log-view/log-view-footer.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/log-view-footer", + "category": "code" + }, + "log-view-header": { + "import": "@aziontech/webkit/log-view-header", + "target": "./src/components/code/log-view/log-view-header.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/log-view-header", + "category": "code" + }, + "code-block": { + "import": "@aziontech/webkit/code-block", + "target": "./src/components/data/code-block/code-block.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/code-block", + "category": "data", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "tabs", + "type": "CodeBlockTab[]", + "default": "[]", + "required": "no", + "doc": "Tab definitions with label, value, code, and optional language, filename, diff, or highlight metadata." + }, + { + "name": "value", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Controlled active tab value (`v-model:value`)." + }, + { + "name": "defaultValue", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Initial active tab when uncontrolled." + }, + { + "name": "showLineNumbers", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Shows a fixed-width gutter with zero-padded line numbers before each code line." + }, + { + "name": "copyAriaLabel", + "type": "string", + "default": "'Copy code'", + "required": "no", + "doc": "Accessible name for the copy IconButton." + }, + { + "name": "animateLines", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Staggered line entrance for website layouts: each line slides from `-8px` with opacity `0 → 1`, `300ms` apart." + } + ], + "events": [ + { + "name": "update:value", + "payload": "string", + "notes": "v-model:value." + }, + { + "name": "value-change", + "payload": "string", + "notes": "Fires when the active tab changes." + }, + { + "name": "copy", + "payload": "string", + "notes": "Fires after the active tab code is copied to the clipboard." + } + ] + }, + "table": { + "import": "@aziontech/webkit/table", + "target": "./src/components/data/table/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/table-root", + "subcomponents": [ + "@aziontech/webkit/table-header", + "@aziontech/webkit/table-body", + "@aziontech/webkit/table-footer", + "@aziontech/webkit/table-caption", + "@aziontech/webkit/table-row", + "@aziontech/webkit/table-head-cell", + "@aziontech/webkit/table-cell", + "@aziontech/webkit/table-sort-button", + "@aziontech/webkit/table-toolbar", + "@aziontech/webkit/table-search" + ], + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "maxHeight", + "type": "string", + "default": "''", + "required": "no", + "doc": "Max height (CSS length). The viewport scrolls dynamically: horizontal scroll appears automatically when columns overflow the width; vertical scroll appears only when `maxHeight` is set smaller than the content (under the sticky header)." + }, + { + "name": "border", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Draw the outer card border around the table. Off by default (the table is typically framed by a surrounding surface); set `border` / `:border=\"…\"` to draw the `var(--border-default)` outline. The internal row dividers are unaffected." + }, + { + "name": "data", + "type": "Record[]", + "default": "[]", + "required": "no", + "doc": "Data-driven mode: row records." + }, + { + "name": "columns", + "type": "TableColumn[]", + "default": "[]", + "required": "no", + "doc": "Data-driven mode: column definitions (`{ id?, accessorKey?, header?, enableSorting?, grow?, principal?, frozen?, width?, align?, kind?, resizable? }`; `width` (px) sets a fixed pixel column (also needed when a frozen column precedes another frozen column on the same edge, so sticky offsets can be summed); a `grow` column without `width` keeps a shared fixed basis so the header and body stay aligned and the cell never wraps. A column with no `grow` defaults to weight `2` for the **principal** column and `1` for the rest, so the columns fill the available space proportionally. The principal column is the one flagged `principal: true`, or — when none is flagged — the first non-action column. A `kind: 'action'` column is automatically pinned to the trailing (right) edge — staying reachable while the rest scrolls horizontally — and is never resizable. **`resizable: true`** opts a column into a live mouse/touch drag-to-resize handle on its header (native pointer events; the `@tanstack/vue-table` exception covers sorting/pagination/selection/visibility, not sizing). On the first drag of any divider every flexible column freezes at its current rendered width; from then on dragging changes **only that column** and the table grows (horizontal scroll) — the others are untouched. The drag floor is the column's own content width (header label + visible body cells), so a column never shrinks past its content. Frozen and action columns ignore `resizable`. When non-empty, the table renders itself via TanStack." + }, + { + "name": "rowKey", + "type": "string", + "default": "'id'", + "required": "no", + "doc": "Field used as the stable row id (for selection)." + }, + { + "name": "enableSorting", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Enable sorting globally; per-column `enableSorting` overrides." + }, + { + "name": "enableRowSelection", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Add a leading selection-checkbox column." + }, + { + "name": "selectOnRowClick", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Data-driven mode: clicking a row toggles its selection (requires `enableRowSelection`); interactive cells still own their own click." + }, + { + "name": "headerVariant", + "type": "'default' | 'compact'", + "default": "'default'", + "required": "no", + "doc": "Header density (data-driven): `compact` shrinks the column-header row's height and padding. Forwarded to the `TableHeader`'s `variant`." + }, + { + "name": "paginated", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Render a Paginator in the footer." + }, + { + "name": "pageSize", + "type": "number", + "default": "10", + "required": "no", + "doc": "Rows per page when paginated." + }, + { + "name": "sorting", + "type": "SortingState", + "default": "undefined", + "required": "no", + "doc": "v-model:sorting (TanStack SortingState)." + }, + { + "name": "rowSelection", + "type": "RowSelectionState", + "default": "undefined", + "required": "no", + "doc": "v-model:rowSelection (TanStack RowSelectionState)." + }, + { + "name": "columnVisibility", + "type": "VisibilityState", + "default": "undefined", + "required": "no", + "doc": "v-model:columnVisibility (TanStack VisibilityState)." + }, + { + "name": "globalFilter", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "v-model:globalFilter text." + }, + { + "name": "pagination", + "type": "PaginationState", + "default": "undefined", + "required": "no", + "doc": "v-model:pagination (TanStack PaginationState)." + }, + { + "name": "manualSorting", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side sorting: emit events, do not sort locally." + }, + { + "name": "manualPagination", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side pagination: emit events, do not slice locally." + }, + { + "name": "manualFiltering", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side filtering: emit events, do not filter locally." + }, + { + "name": "rowCount", + "type": "number", + "default": "undefined", + "required": "no", + "doc": "Total row count for manual pagination." + }, + { + "name": "rowsPerPageOptions", + "type": "number[]", + "default": "[10, 25, 50, 100]", + "required": "no", + "doc": "Rows-per-page options shown in the footer selector." + } + ], + "events": [ + { + "name": "update:sorting", + "payload": "value: SortingState", + "notes": "v-model:sorting." + }, + { + "name": "update:rowSelection", + "payload": "value: RowSelectionState", + "notes": "v-model:rowSelection." + }, + { + "name": "update:columnVisibility", + "payload": "value: VisibilityState", + "notes": "v-model:columnVisibility." + }, + { + "name": "update:globalFilter", + "payload": "value: string", + "notes": "v-model:globalFilter." + }, + { + "name": "update:pagination", + "payload": "value: PaginationState", + "notes": "v-model:pagination." + }, + { + "name": "row-click", + "payload": "row: Record", + "notes": "Fires when a rendered row is clicked (data-driven mode)." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "footer", + "notes": "—" + }, + { + "name": "caption", + "notes": "—" + }, + { + "name": "title", + "notes": "—" + }, + { + "name": "toolbar", + "notes": "{ table }" + }, + { + "name": "filters", + "notes": "{ table }" + }, + { + "name": "empty", + "notes": "—" + } + ] + }, + "table-root": { + "import": "@aziontech/webkit/table-root", + "target": "./src/components/data/table/table.vue", + "kind": "root", + "rootOf": "table", + "treeShakeableImport": "@aziontech/webkit/table-root", + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "maxHeight", + "type": "string", + "default": "''", + "required": "no", + "doc": "Max height (CSS length). The viewport scrolls dynamically: horizontal scroll appears automatically when columns overflow the width; vertical scroll appears only when `maxHeight` is set smaller than the content (under the sticky header)." + }, + { + "name": "border", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Draw the outer card border around the table. Off by default (the table is typically framed by a surrounding surface); set `border` / `:border=\"…\"` to draw the `var(--border-default)` outline. The internal row dividers are unaffected." + }, + { + "name": "data", + "type": "Record[]", + "default": "[]", + "required": "no", + "doc": "Data-driven mode: row records." + }, + { + "name": "columns", + "type": "TableColumn[]", + "default": "[]", + "required": "no", + "doc": "Data-driven mode: column definitions (`{ id?, accessorKey?, header?, enableSorting?, grow?, principal?, frozen?, width?, align?, kind?, resizable? }`; `width` (px) sets a fixed pixel column (also needed when a frozen column precedes another frozen column on the same edge, so sticky offsets can be summed); a `grow` column without `width` keeps a shared fixed basis so the header and body stay aligned and the cell never wraps. A column with no `grow` defaults to weight `2` for the **principal** column and `1` for the rest, so the columns fill the available space proportionally. The principal column is the one flagged `principal: true`, or — when none is flagged — the first non-action column. A `kind: 'action'` column is automatically pinned to the trailing (right) edge — staying reachable while the rest scrolls horizontally — and is never resizable. **`resizable: true`** opts a column into a live mouse/touch drag-to-resize handle on its header (native pointer events; the `@tanstack/vue-table` exception covers sorting/pagination/selection/visibility, not sizing). On the first drag of any divider every flexible column freezes at its current rendered width; from then on dragging changes **only that column** and the table grows (horizontal scroll) — the others are untouched. The drag floor is the column's own content width (header label + visible body cells), so a column never shrinks past its content. Frozen and action columns ignore `resizable`. When non-empty, the table renders itself via TanStack." + }, + { + "name": "rowKey", + "type": "string", + "default": "'id'", + "required": "no", + "doc": "Field used as the stable row id (for selection)." + }, + { + "name": "enableSorting", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Enable sorting globally; per-column `enableSorting` overrides." + }, + { + "name": "enableRowSelection", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Add a leading selection-checkbox column." + }, + { + "name": "selectOnRowClick", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Data-driven mode: clicking a row toggles its selection (requires `enableRowSelection`); interactive cells still own their own click." + }, + { + "name": "headerVariant", + "type": "'default' | 'compact'", + "default": "'default'", + "required": "no", + "doc": "Header density (data-driven): `compact` shrinks the column-header row's height and padding. Forwarded to the `TableHeader`'s `variant`." + }, + { + "name": "paginated", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Render a Paginator in the footer." + }, + { + "name": "pageSize", + "type": "number", + "default": "10", + "required": "no", + "doc": "Rows per page when paginated." + }, + { + "name": "sorting", + "type": "SortingState", + "default": "undefined", + "required": "no", + "doc": "v-model:sorting (TanStack SortingState)." + }, + { + "name": "rowSelection", + "type": "RowSelectionState", + "default": "undefined", + "required": "no", + "doc": "v-model:rowSelection (TanStack RowSelectionState)." + }, + { + "name": "columnVisibility", + "type": "VisibilityState", + "default": "undefined", + "required": "no", + "doc": "v-model:columnVisibility (TanStack VisibilityState)." + }, + { + "name": "globalFilter", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "v-model:globalFilter text." + }, + { + "name": "pagination", + "type": "PaginationState", + "default": "undefined", + "required": "no", + "doc": "v-model:pagination (TanStack PaginationState)." + }, + { + "name": "manualSorting", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side sorting: emit events, do not sort locally." + }, + { + "name": "manualPagination", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side pagination: emit events, do not slice locally." + }, + { + "name": "manualFiltering", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Server-side filtering: emit events, do not filter locally." + }, + { + "name": "rowCount", + "type": "number", + "default": "undefined", + "required": "no", + "doc": "Total row count for manual pagination." + }, + { + "name": "rowsPerPageOptions", + "type": "number[]", + "default": "[10, 25, 50, 100]", + "required": "no", + "doc": "Rows-per-page options shown in the footer selector." + } + ], + "events": [ + { + "name": "update:sorting", + "payload": "value: SortingState", + "notes": "v-model:sorting." + }, + { + "name": "update:rowSelection", + "payload": "value: RowSelectionState", + "notes": "v-model:rowSelection." + }, + { + "name": "update:columnVisibility", + "payload": "value: VisibilityState", + "notes": "v-model:columnVisibility." + }, + { + "name": "update:globalFilter", + "payload": "value: string", + "notes": "v-model:globalFilter." + }, + { + "name": "update:pagination", + "payload": "value: PaginationState", + "notes": "v-model:pagination." + }, + { + "name": "row-click", + "payload": "row: Record", + "notes": "Fires when a rendered row is clicked (data-driven mode)." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "footer", + "notes": "—" + }, + { + "name": "caption", + "notes": "—" + }, + { + "name": "title", + "notes": "—" + }, + { + "name": "toolbar", + "notes": "{ table }" + }, + { + "name": "filters", + "notes": "{ table }" + }, + { + "name": "empty", + "notes": "—" + } + ] + }, + "table-header": { + "import": "@aziontech/webkit/table-header", + "target": "./src/components/data/table/table-header/table-header.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-header" + }, + "table-body": { + "import": "@aziontech/webkit/table-body", + "target": "./src/components/data/table/table-body/table-body.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-body" + }, + "table-footer": { + "import": "@aziontech/webkit/table-footer", + "target": "./src/components/data/table/table-footer/table-footer.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-footer" + }, + "table-caption": { + "import": "@aziontech/webkit/table-caption", + "target": "./src/components/data/table/table-caption/table-caption.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-caption" + }, + "table-row": { + "import": "@aziontech/webkit/table-row", + "target": "./src/components/data/table/table-row/table-row.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-row" + }, + "table-head-cell": { + "import": "@aziontech/webkit/table-head-cell", + "target": "./src/components/data/table/table-head-cell/table-head-cell.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-head-cell" + }, + "table-cell": { + "import": "@aziontech/webkit/table-cell", + "target": "./src/components/data/table/table-cell/table-cell.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-cell" + }, + "table-sort-button": { + "import": "@aziontech/webkit/table-sort-button", + "target": "./src/components/data/table/table-sort-button/table-sort-button.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-sort-button" + }, + "table-toolbar": { + "import": "@aziontech/webkit/table-toolbar", + "target": "./src/components/data/table/table-toolbar/table-toolbar.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-toolbar" + }, + "table-search": { + "import": "@aziontech/webkit/table-search", + "target": "./src/components/data/table/table-search/table-search.vue", + "kind": "subcomponent", + "parent": "table", + "treeShakeableImport": "@aziontech/webkit/table-search" + }, + "paginator": { + "import": "@aziontech/webkit/paginator", + "target": "./src/components/data/paginator/index.ts", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/paginator", + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "page", + "type": "number", + "default": "1", + "required": "no", + "doc": "Current page, 1-based (data-driven mode). Supports `v-model:page`." + }, + { + "name": "total", + "type": "number", + "default": "undefined", + "required": "no", + "doc": "Total item count. When set, the Paginator runs in data-driven mode and renders its own controls." + }, + { + "name": "pageSize", + "type": "number", + "default": "10", + "required": "no", + "doc": "Rows per page (data-driven mode). Supports `v-model:page-size`." + }, + { + "name": "pageSizeOptions", + "type": "number[]", + "default": "[10, 25, 50, 100]", + "required": "no", + "doc": "Page-size options offered in the rows-per-page selector." + }, + { + "name": "siblingCount", + "type": "number", + "default": "1", + "required": "no", + "doc": "Page numbers shown on each side of the current page before collapsing into an overflow ellipsis." + }, + { + "name": "ariaLabel", + "type": "string", + "default": "'Pagination'", + "required": "no", + "doc": "Accessible name for the navigation landmark." + } + ], + "events": [ + { + "name": "update:page", + "payload": "[page: number]", + "notes": "Current page changed in data-driven mode (Previous / Next / page number). Supports `v-model:page`." + }, + { + "name": "update:pageSize", + "payload": "[pageSize: number]", + "notes": "Rows-per-page changed via the internal selector. Supports `v-model:page-size`." + }, + { + "name": "page-change", + "payload": "[page: number]", + "notes": "Convenience action event fired alongside `update:page` whenever the page changes." + } + ], + "slots": [ + { + "name": "info", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + }, + { + "name": "controls", + "notes": "—" + } + ] + }, + "pagination-button": { + "import": "@aziontech/webkit/pagination-button", + "target": "./src/components/data/paginator/pagination-button/pagination-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/pagination-button", + "category": "data" + }, + "paginator-info": { + "import": "@aziontech/webkit/paginator-info", + "target": "./src/components/data/paginator/paginator-info/paginator-info.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/paginator-info", + "category": "data" + }, + "paginator-page-size": { + "import": "@aziontech/webkit/paginator-page-size", + "target": "./src/components/data/paginator/paginator-page-size/paginator-page-size.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/paginator-page-size", + "category": "data" + }, + "flow": { + "import": "@aziontech/webkit/flow", + "target": "./src/components/data/flow/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/flow-root", + "subcomponents": [ + "@aziontech/webkit/flow-node", + "@aziontech/webkit/flow-parallel", + "@aziontech/webkit/flow-anchor" + ], + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "align", + "type": "'start' \\ | 'center'", + "default": "'start'", + "required": "no", + "doc": "Vertical alignment of nodes within the diagram." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "flow-root": { + "import": "@aziontech/webkit/flow-root", + "target": "./src/components/data/flow/flow.vue", + "kind": "root", + "rootOf": "flow", + "treeShakeableImport": "@aziontech/webkit/flow-root", + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "align", + "type": "'start' \\ | 'center'", + "default": "'start'", + "required": "no", + "doc": "Vertical alignment of nodes within the diagram." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "flow-node": { + "import": "@aziontech/webkit/flow-node", + "target": "./src/components/data/flow/flow-node/flow-node.vue", + "kind": "subcomponent", + "parent": "flow", + "treeShakeableImport": "@aziontech/webkit/flow-node" + }, + "flow-parallel": { + "import": "@aziontech/webkit/flow-parallel", + "target": "./src/components/data/flow/flow-parallel/flow-parallel.vue", + "kind": "subcomponent", + "parent": "flow", + "treeShakeableImport": "@aziontech/webkit/flow-parallel" + }, + "flow-anchor": { + "import": "@aziontech/webkit/flow-anchor", + "target": "./src/components/data/flow/flow-anchor/flow-anchor.vue", + "kind": "subcomponent", + "parent": "flow", + "treeShakeableImport": "@aziontech/webkit/flow-anchor" + }, + "pick-list": { + "import": "@aziontech/webkit/pick-list", + "target": "./src/components/data/pick-list/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/pick-list-root", + "subcomponents": [ + "@aziontech/webkit/pick-list-source", + "@aziontech/webkit/pick-list-target", + "@aziontech/webkit/pick-list-controls" + ], + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "modelValue", + "type": "[unknown[], unknown[]]", + "default": "() => [[], []]", + "required": "no", + "doc": "Bound pair of lists as `[sourceItems, targetItems]` (v-model)." + }, + { + "name": "dataKey", + "type": "string", + "default": "''", + "required": "no", + "doc": "Item field that uniquely identifies a record; enables selection tracking and stable keys." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables all selection and move controls and applies disabled tokens." + }, + { + "name": "moveOnDoubleClick", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, double-clicking an item moves it to the opposite list. Set false to keep `item-double-click` firing without the move." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "[unknown[], unknown[]]", + "notes": "v-model update with the new `[sourceItems, targetItems]` pair." + }, + { + "name": "move", + "payload": "{ direction: 'to-target' | 'to-source'; items: unknown[] }", + "notes": "Fired after items move between lists, with the moved items." + }, + { + "name": "item-double-click", + "payload": "{ list: 'source' | 'target'; item: unknown; index: number }", + "notes": "Fired when an option is double-clicked, with the item, its index, and which list it was in. Fires regardless of `moveOnDoubleClick`, but not while the component is disabled or a list is loading." + } + ], + "slots": [ + { + "name": "default", + "notes": "{ move, moveAll, hasSelection, count, disabled, loading }" + } + ] + }, + "pick-list-root": { + "import": "@aziontech/webkit/pick-list-root", + "target": "./src/components/data/pick-list/pick-list.vue", + "kind": "root", + "rootOf": "pick-list", + "treeShakeableImport": "@aziontech/webkit/pick-list-root", + "category": "data", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "modelValue", + "type": "[unknown[], unknown[]]", + "default": "() => [[], []]", + "required": "no", + "doc": "Bound pair of lists as `[sourceItems, targetItems]` (v-model)." + }, + { + "name": "dataKey", + "type": "string", + "default": "''", + "required": "no", + "doc": "Item field that uniquely identifies a record; enables selection tracking and stable keys." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables all selection and move controls and applies disabled tokens." + }, + { + "name": "moveOnDoubleClick", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, double-clicking an item moves it to the opposite list. Set false to keep `item-double-click` firing without the move." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "[unknown[], unknown[]]", + "notes": "v-model update with the new `[sourceItems, targetItems]` pair." + }, + { + "name": "move", + "payload": "{ direction: 'to-target' | 'to-source'; items: unknown[] }", + "notes": "Fired after items move between lists, with the moved items." + }, + { + "name": "item-double-click", + "payload": "{ list: 'source' | 'target'; item: unknown; index: number }", + "notes": "Fired when an option is double-clicked, with the item, its index, and which list it was in. Fires regardless of `moveOnDoubleClick`, but not while the component is disabled or a list is loading." + } + ], + "slots": [ + { + "name": "default", + "notes": "{ move, moveAll, hasSelection, count, disabled, loading }" + } + ] + }, + "pick-list-source": { + "import": "@aziontech/webkit/pick-list-source", + "target": "./src/components/data/pick-list/pick-list-source/pick-list-source.vue", + "kind": "subcomponent", + "parent": "pick-list", + "treeShakeableImport": "@aziontech/webkit/pick-list-source" + }, + "pick-list-target": { + "import": "@aziontech/webkit/pick-list-target", + "target": "./src/components/data/pick-list/pick-list-target/pick-list-target.vue", + "kind": "subcomponent", + "parent": "pick-list", + "treeShakeableImport": "@aziontech/webkit/pick-list-target" + }, + "pick-list-controls": { + "import": "@aziontech/webkit/pick-list-controls", + "target": "./src/components/data/pick-list/pick-list-controls/pick-list-controls.vue", + "kind": "subcomponent", + "parent": "pick-list", + "treeShakeableImport": "@aziontech/webkit/pick-list-controls" + }, + "message": { + "import": "@aziontech/webkit/message", + "target": "./src/components/feedback/message/message.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/message", + "category": "feedback", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "severity", + "type": "'info' | 'success' | 'warning' | 'danger' | 'error'", + "default": "'info'", + "required": "no", + "doc": "Visual severity variant (maps Error to danger)." + }, + { + "name": "title", + "type": "string", + "default": "—", + "required": "yes", + "doc": "Primary message heading." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Supporting body copy below the title." + }, + { + "name": "icon", + "type": "string", + "default": "''", + "required": "no", + "doc": "PrimeIcons class override for the leading icon." + }, + { + "name": "actionLabel", + "type": "string", + "default": "''", + "required": "no", + "doc": "Label for the built-in text action button; hidden when empty." + }, + { + "name": "closable", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, shows a close control that dismisses the message." + }, + { + "name": "life", + "type": "number", + "default": "0", + "required": "no", + "doc": "Duration in milliseconds before auto-dismiss; `0` disables auto-dismiss." + } + ], + "events": [ + { + "name": "action", + "payload": "MouseEvent", + "notes": "Emitted when the built-in action button is clicked." + }, + { + "name": "close", + "payload": "void", + "notes": "Emitted when the message is dismissed manually or after `life` expires." + } + ], + "slots": [ + { + "name": "action", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + } + ] + }, + "empty-state": { + "import": "@aziontech/webkit/empty-state", + "target": "./src/components/feedback/empty-state/empty-state.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/empty-state", + "category": "feedback", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "title", + "type": "string", + "default": "—", + "required": "yes", + "doc": "Primary heading announcing the empty resource." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Supporting body copy below the title." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects the illustration size, the surrounding padding, and the gaps between illustration, text, and actions." + }, + { + "name": "bordered", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, wraps the content in a bordered surface card; otherwise renders on a transparent background." + } + ], + "slots": [ + { + "name": "icon", + "notes": "—" + }, + { + "name": "actions", + "notes": "—" + } + ] + }, + "status-indicator": { + "import": "@aziontech/webkit/status-indicator", + "target": "./src/components/feedback/status-indicator/status-indicator.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/status-indicator", + "category": "feedback", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "status", + "type": "'positive' | 'info' | 'neutral' | 'warning' | 'alt' | 'danger'", + "default": "'positive'", + "required": "no", + "doc": "status." + }, + { + "name": "loading", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Shows loading state and disables activation." + }, + { + "name": "label", + "type": "string", + "default": "'Status'", + "required": "no", + "doc": "Visible label text." + } + ] + }, + "toast": { + "import": "@aziontech/webkit/toast", + "target": "./src/components/feedback/toast/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/toast-root", + "subcomponents": [ + "@aziontech/webkit/toast-item", + "@aziontech/webkit/toast-title", + "@aziontech/webkit/toast-description", + "@aziontech/webkit/toast-action", + "@aziontech/webkit/toast-close" + ], + "category": "feedback", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "position", + "type": "'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'", + "default": "'bottom-right'", + "required": "no", + "doc": "Corner (or edge-center) the stack is anchored to." + }, + { + "name": "duration", + "type": "number", + "default": "4000", + "required": "no", + "doc": "Default auto-dismiss time in ms each toast inherits; a per-toast `duration` overrides it, and `0` keeps the toast until dismissed." + }, + { + "name": "max", + "type": "number", + "default": "3", + "required": "no", + "doc": "Maximum simultaneously visible toasts per corner before the rest queue behind." + }, + { + "name": "expand", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Lay the stack out fully expanded with a gap; when `false` the resting stack overlaps into a peek." + }, + { + "name": "closable", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Show an always-visible close control on every toast; a per-toast `closable` option overrides it." + } + ], + "slots": [ + { + "name": "default", + "notes": "{ toast, dismiss }" + } + ] + }, + "toast-root": { + "import": "@aziontech/webkit/toast-root", + "target": "./src/components/feedback/toast/toaster.vue", + "kind": "root", + "rootOf": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-root", + "category": "feedback", + "structure": "composition", + "status": "approved", + "props": [ + { + "name": "position", + "type": "'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'", + "default": "'bottom-right'", + "required": "no", + "doc": "Corner (or edge-center) the stack is anchored to." + }, + { + "name": "duration", + "type": "number", + "default": "4000", + "required": "no", + "doc": "Default auto-dismiss time in ms each toast inherits; a per-toast `duration` overrides it, and `0` keeps the toast until dismissed." + }, + { + "name": "max", + "type": "number", + "default": "3", + "required": "no", + "doc": "Maximum simultaneously visible toasts per corner before the rest queue behind." + }, + { + "name": "expand", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Lay the stack out fully expanded with a gap; when `false` the resting stack overlaps into a peek." + }, + { + "name": "closable", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Show an always-visible close control on every toast; a per-toast `closable` option overrides it." + } + ], + "slots": [ + { + "name": "default", + "notes": "{ toast, dismiss }" + } + ] + }, + "toast-item": { + "import": "@aziontech/webkit/toast-item", + "target": "./src/components/feedback/toast/toast-item/toast-item.vue", + "kind": "subcomponent", + "parent": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-item" + }, + "toast-title": { + "import": "@aziontech/webkit/toast-title", + "target": "./src/components/feedback/toast/toast-title/toast-title.vue", + "kind": "subcomponent", + "parent": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-title" + }, + "toast-description": { + "import": "@aziontech/webkit/toast-description", + "target": "./src/components/feedback/toast/toast-description/toast-description.vue", + "kind": "subcomponent", + "parent": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-description" + }, + "toast-action": { + "import": "@aziontech/webkit/toast-action", + "target": "./src/components/feedback/toast/toast-action/toast-action.vue", + "kind": "subcomponent", + "parent": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-action" + }, + "toast-close": { + "import": "@aziontech/webkit/toast-close", + "target": "./src/components/feedback/toast/toast-close/toast-close.vue", + "kind": "subcomponent", + "parent": "toast", + "treeShakeableImport": "@aziontech/webkit/toast-close" + }, + "skeleton": { + "import": "@aziontech/webkit/skeleton", + "target": "./src/components/feedback/skeleton/skeleton.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/skeleton", + "category": "feedback", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "kind", + "type": "'shape' | 'circle'", + "default": "'shape'", + "required": "no", + "doc": "Geometry: a rounded rectangular block (`shape`) or a circle." + }, + { + "name": "width", + "type": "string", + "default": "'100%'", + "required": "no", + "doc": "CSS width (any length)." + }, + { + "name": "height", + "type": "string", + "default": "'1rem'", + "required": "no", + "doc": "CSS height (any length); for a circle, set equal to `width`." + }, + { + "name": "animated", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Shimmer while loading; suppressed under reduced motion." + } + ] + }, + "progress-bar": { + "import": "@aziontech/webkit/progress-bar", + "target": "./src/components/feedback/progress-bar/progress-bar.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/progress-bar", + "category": "feedback", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "value", + "type": "number", + "default": "0", + "required": "no", + "doc": "Current progress, relative to `max`." + }, + { + "name": "max", + "type": "number", + "default": "100", + "required": "no", + "doc": "Upper bound; percentage = `value / max * 100`." + }, + { + "name": "shape", + "type": "'rounded' | 'flat'", + "default": "'rounded'", + "required": "no", + "doc": "Track and fill corner-radius variant." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Bar height token." + }, + { + "name": "indeterminate", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Loading state; animates a sliding segment and ignores `value`." + } + ] + }, + "calendar": { + "import": "@aziontech/webkit/calendar", + "target": "./src/components/inputs/calendar/calendar.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/calendar", + "category": "inputs", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "modelValue", + "type": "Date | null | CalendarRange", + "default": "null", + "required": "no", + "doc": "Selected value for v-model. A `Date` (or null) in single mode; a `{ start, end }` range object in range mode." + }, + { + "name": "mode", + "type": "'single' | 'range'", + "default": "'single'", + "required": "no", + "doc": "Selection mode. Single picks one date; range picks a start and end date." + }, + { + "name": "min", + "type": "Date | undefined", + "default": "undefined", + "required": "no", + "doc": "Earliest selectable date; earlier days render disabled." + }, + { + "name": "max", + "type": "Date | undefined", + "default": "undefined", + "required": "no", + "doc": "Latest selectable date; later days render disabled." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the whole grid and navigation, applying disabled tokens." + }, + { + "name": "showHeader", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Shows the month/year label and previous/next month navigation." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "Date | null | CalendarRange", + "notes": "v-model. The selected `Date` (single) or `{ start, end }` (range)." + }, + { + "name": "month-change", + "payload": "CalendarMonth", + "notes": "Emitted when the visible month changes via navigation or keyboard paging. Payload is `{ year, month }` (month is 0-indexed)." + } + ] + }, + "input-text": { + "import": "@aziontech/webkit/input-text", + "target": "./src/components/inputs/input-text/input-text.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/input-text", + "category": "inputs", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Two-way bound value of the field." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder shown when the field is empty." + }, + { + "name": "type", + "type": "'text' | 'email' | 'number'", + "default": "'text'", + "required": "no", + "doc": "Native input type. Restricted to single-line variants the field treats identically." + }, + { + "name": "maxLength", + "type": "number | undefined", + "default": "undefined", + "required": "no", + "doc": "Native `maxlength` — maximum number of characters allowed." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects height only — padding and typography are constant. Heights: small=28px, medium=32px, large=40px." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; value is visible but not editable. Native pass-through." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets native `required` and `aria-required`. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid`." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "Emitted on native `input` event with the new value." + } + ], + "slots": [ + { + "name": "iconLeft", + "notes": "Leading icon rendered inside the field, before the input." + }, + { + "name": "iconRight", + "notes": "Trailing icon rendered inside the field, after the input." + } + ] + }, + "input-number": { + "import": "@aziontech/webkit/input-number", + "target": "./src/components/inputs/input-number/input-number.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/input-number", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "number", + "default": "0", + "required": "no", + "doc": "v-model value (always numeric; cleared input falls back to `min ?? 0`)." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Text shown when the input is empty." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects height, padding, and typography." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as read-only; spinner buttons are inert." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border/ring tokens." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required for assistive tech." + }, + { + "name": "min", + "type": "number | undefined", + "default": "undefined", + "required": "no", + "doc": "Minimum allowed value; spinner stops at this bound." + }, + { + "name": "max", + "type": "number | undefined", + "default": "undefined", + "required": "no", + "doc": "Maximum allowed value; spinner stops at this bound." + }, + { + "name": "step", + "type": "number", + "default": "1", + "required": "no", + "doc": "Increment used by the spinner buttons and arrow keys." + }, + { + "name": "showButtons", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Renders the chevron-up/chevron-down spinner controls." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "number", + "notes": "Emitted on every value change (typing or spinner)." + }, + { + "name": "change", + "payload": "number", + "notes": "Emitted when the user commits the value (blur or Enter)." + } + ], + "slots": [ + { + "name": "prefix", + "notes": "—" + }, + { + "name": "suffix", + "notes": "—" + } + ] + }, + "field-text": { + "import": "@aziontech/webkit/field-text", + "target": "./src/components/inputs/field-text/field-text.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-text", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Two-way bound value of the underlying `InputText`." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Text rendered inside the `Label`. When empty, the label row is omitted." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder forwarded to the `InputText`." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Auxiliary text rendered inside `HelperText`. When empty, the helper row is omitted **except** when `disabled` is true — in that case the component falls back to a default disabled message so the lock icon always has matching copy." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size forwarded to the `InputText`. Heights: small=28px, medium=32px, large=40px." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the input and switches the helper to `kind=\"disabled\"` (lock icon)." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the input read-only; value is visible but not editable. Native pass-through." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Adds the `Required` tag to the `Label` and sets native `required` + `aria-required` on the input." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Switches the helper to `kind=\"invalid\"` and applies invalid border/ring tokens on the input." + }, + { + "name": "inputId", + "type": "string", + "default": "''", + "required": "no", + "doc": "id for the native input; consumed by `Label` via `for` and by `aria-describedby` wiring. When empty, the component auto-generates an id via `useId()`." + }, + { + "name": "name", + "type": "string", + "default": "''", + "required": "no", + "doc": "HTML name for the underlying input (form + vee-validate integration)." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "Re-emitted from the underlying `InputText` on every input event." + } + ], + "slots": [ + { + "name": "iconLeft", + "notes": "—" + }, + { + "name": "iconRight", + "notes": "—" + } + ] + }, + "helper-text": { + "import": "@aziontech/webkit/helper-text", + "target": "./src/components/inputs/helper-text/helper-text.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/helper-text", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "value", + "type": "string", + "default": "''", + "required": "no", + "doc": "Fallback text when the default slot is empty." + }, + { + "name": "kind", + "type": "'helper' | 'invalid' | 'required' | 'disabled'", + "default": "'helper'", + "required": "no", + "doc": "Visual variant; `disabled` also prepends a `pi pi-lock` icon." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "label": { + "import": "@aziontech/webkit/label", + "target": "./src/components/inputs/label/label.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/label", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "value", + "type": "string", + "default": "''", + "required": "no", + "doc": "Fallback text when the default slot is empty." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Appends a `Required` tag next to the label text." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "input-password": { + "import": "@aziontech/webkit/input-password", + "target": "./src/components/inputs/input-password/input-password.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/input-password", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Two-way bound value of the field." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder shown when the field is empty." + }, + { + "name": "maxLength", + "type": "number | undefined", + "default": "undefined", + "required": "no", + "doc": "Native `maxlength` — maximum number of characters allowed." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens. The toggle button inherits the disabled state." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; value is visible but not editable. Toggle button stays operable." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets native `required` and `aria-required`. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid`." + }, + { + "name": "toggleable", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, renders the visibility toggle button on the trailing edge of the field. When false, the field behaves as a plain password input with no toggle and the `iconRight` slot becomes available." + }, + { + "name": "autocomplete", + "type": "'current-password' | 'new-password' | 'off'", + "default": "'current-password'", + "required": "no", + "doc": "Native `autocomplete` hint for password managers. Use `new-password` for sign-up and password-change flows." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "Emitted on native `input` event with the new value." + } + ], + "slots": [ + { + "name": "iconLeft", + "notes": "—" + }, + { + "name": "iconRight", + "notes": "—" + } + ] + }, + "field-password": { + "import": "@aziontech/webkit/field-password", + "target": "./src/components/inputs/field-password/field-password.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-password", + "category": "inputs", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Two-way bound value of the underlying `InputPassword`." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Text rendered inside the `Label`. When empty, the label row is omitted." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder forwarded to the `InputPassword`." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Auxiliary text rendered inside `HelperText`. When empty, the helper row is omitted **except** when `disabled` is true — in that case the component falls back to a default disabled message so the lock icon always has matching copy." + }, + { + "name": "maxLength", + "type": "number | undefined", + "default": "undefined", + "required": "no", + "doc": "Native `maxlength` forwarded to the `InputPassword`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the input and switches the helper to `kind=\"disabled\"` (lock icon)." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the input read-only; value is visible but not editable. Native pass-through." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Adds the `Required` tag to the `Label` and sets native `required` + `aria-required` on the input." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Switches the helper to `kind=\"invalid\"` and applies invalid border/ring tokens on the input." + }, + { + "name": "toggleable", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Forwards to `InputPassword`. When true, renders the visibility toggle on the trailing edge; when false, the field behaves as a plain password input." + }, + { + "name": "autocomplete", + "type": "'current-password' | 'new-password' | 'off'", + "default": "'current-password'", + "required": "no", + "doc": "Forwarded to the `InputPassword` for password-manager hints. Use `new-password` for sign-up and password-change flows." + }, + { + "name": "inputId", + "type": "string", + "default": "''", + "required": "no", + "doc": "id for the native input; consumed by `Label` via `for` and by `aria-describedby` wiring." + }, + { + "name": "name", + "type": "string", + "default": "''", + "required": "no", + "doc": "HTML name for the underlying input (form + vee-validate integration)." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "Re-emitted from the underlying `InputPassword` on every input event." + } + ], + "slots": [ + { + "name": "iconLeft", + "notes": "—" + }, + { + "name": "iconRight", + "notes": "—" + } + ] + }, + "box-grid-selection": { + "import": "@aziontech/webkit/box-grid-selection", + "target": "./src/components/inputs/box-grid-selection/box-grid-selection.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/box-grid-selection", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string | number | undefined", + "default": "undefined", + "required": "no", + "doc": "Currently selected item value (v-model)." + }, + { + "name": "items", + "type": "BoxGridSelectionItem[]", + "default": "undefined", + "required": "yes", + "doc": "Options rendered as selectable cards; each entry must include `value` and `label`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables all options and applies disabled tokens." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string | number", + "notes": "v-model when the user selects an option." + } + ], + "slots": [ + { + "name": "default", + "notes": "{ item: BoxGridSelectionItem; selected: boolean }" + }, + { + "name": "tag", + "notes": "{ item: BoxGridSelectionItem; selected: boolean }" + } + ] + }, + "chip": { + "import": "@aziontech/webkit/chip", + "target": "./src/components/inputs/chip/chip.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/chip", + "category": "inputs", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Fallback text when the default slot is empty." + }, + { + "name": "size", + "type": "'small' | 'medium'", + "default": "'medium'", + "required": "no", + "doc": "Size token; `medium` is 24px tall, `small` is 20px." + }, + { + "name": "removable", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, renders a trailing remove button that emits `remove`." + } + ], + "events": [ + { + "name": "remove", + "payload": "MouseEvent", + "notes": "Fires when `removable` is true and the remove button is activated (click / Enter / Space), after the chip's exit (fade-out) animation completes, so the parent removes the chip once it has animated out." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "checkbox": { + "import": "@aziontech/webkit/checkbox", + "target": "./src/components/inputs/checkbox/checkbox.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/checkbox", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "unknown", + "default": "undefined", + "required": "no", + "doc": "Two-way bound value. Boolean in `binary` mode, scalar when paired with `value`, array when multi-selecting." + }, + { + "name": "value", + "type": "unknown", + "default": "undefined", + "required": "no", + "doc": "Identifier for this checkbox in non-binary mode. Compared against `modelValue` (or included in the array)." + }, + { + "name": "binary", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, the checkbox toggles `modelValue` as a boolean (no `value` pairing)." + }, + { + "name": "indeterminate", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Renders the indeterminate visual (horizontal bar). Does not affect `modelValue`; the parent owns the tri-state logic." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; value is visible but cannot change via interaction." + }, + { + "name": "inputId", + "type": "string", + "default": "''", + "required": "no", + "doc": "Forwarded to the inner `` for label association." + }, + { + "name": "name", + "type": "string", + "default": "''", + "required": "no", + "doc": "HTML name for form submission." + }, + { + "name": "tabindex", + "type": "number", + "default": "0", + "required": "no", + "doc": "Forwarded to the inner ``." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "unknown", + "notes": "Fires on activation. Payload is the next boolean (binary mode) or the next array (multi mode) or the matched value (single-value mode)." + } + ] + }, + "field-checkbox": { + "import": "@aziontech/webkit/field-checkbox", + "target": "./src/components/inputs/field-checkbox/field-checkbox.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-checkbox", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "trueValue", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Value emitted when checked." + }, + { + "name": "falseValue", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Value emitted when unchecked." + }, + { + "name": "name", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "HTML name for form and vee-validate integration." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "inputId", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "id for the native input; links label to control." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "boolean", + "notes": "v-model." + } + ] + }, + "field-checkbox-block": { + "import": "@aziontech/webkit/field-checkbox-block", + "target": "./src/components/inputs/field-checkbox-block/field-checkbox-block.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-checkbox-block", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "trueValue", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Value emitted when checked." + }, + { + "name": "falseValue", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Value emitted when unchecked." + }, + { + "name": "name", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "HTML name for form and vee-validate integration." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "inputId", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "id for the native input; links label to control." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "boolean", + "notes": "v-model." + } + ] + }, + "field-radio": { + "import": "@aziontech/webkit/field-radio", + "target": "./src/components/inputs/field-radio/field-radio.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-radio", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "value", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Option value for this radio instance." + }, + { + "name": "name", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "HTML name shared across a mutually exclusive group." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "inputId", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "id for the native input; links label to control." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "v-model." + } + ] + }, + "field-radio-block": { + "import": "@aziontech/webkit/field-radio-block", + "target": "./src/components/inputs/field-radio-block/field-radio-block.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-radio-block", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "value", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Option value for this radio instance." + }, + { + "name": "name", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "HTML name shared across a mutually exclusive group." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "inputId", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "id for the native input; links label to control." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "v-model." + } + ] + }, + "field-switch": { + "import": "@aziontech/webkit/field-switch", + "target": "./src/components/inputs/field-switch/field-switch.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-switch", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "boolean", + "notes": "v-model." + } + ] + }, + "field-switch-block": { + "import": "@aziontech/webkit/field-switch-block", + "target": "./src/components/inputs/field-switch-block/field-switch-block.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-switch-block", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Primary label text." + }, + { + "name": "description", + "type": "string", + "default": "''", + "required": "no", + "doc": "Secondary description." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Helper badge text shown when disabled." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "boolean", + "notes": "v-model." + } + ] + }, + "radio-button": { + "import": "@aziontech/webkit/radio-button", + "target": "./src/components/inputs/radio-button/radio-button.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/radio-button", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Selected value for v-model." + }, + { + "name": "value", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Option value for this radio instance." + }, + { + "name": "name", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "HTML name shared across a mutually exclusive group." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "inputId", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "id for the native input; links label to control." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "v-model." + } + ] + }, + "switch": { + "import": "@aziontech/webkit/switch", + "target": "./src/components/inputs/switch/switch.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/switch", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "isToggled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Toggled-on state. Bind with `v-model:isToggled=\"value\"`. Mirrors the Figma `isToggled` variant." + }, + { + "name": "type", + "type": "'default' | 'privacy'", + "default": "'default'", + "required": "no", + "doc": "Visual variant. `privacy` renders a lock icon inside the handle (closed when off, open when on)." + }, + { + "name": "isFocused", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Forces the focused visual state regardless of keyboard focus. Mirrors the Figma `isFocused` variant." + } + ], + "events": [ + { + "name": "update:isToggled", + "payload": "boolean", + "notes": "Emitted when the user toggles the switch. Paired with `v-model:isToggled`." + } + ] + }, + "textarea": { + "import": "@aziontech/webkit/textarea", + "target": "./src/components/inputs/textarea/textarea.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/textarea", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Bound value (v-model)." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder shown when the field is empty." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as read-only; keeps focus styling, blocks edits." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies invalid styling and sets `aria-invalid`. Combined with `required`, switches to warning border tone." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required and sets `aria-required`." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "v-model update." + } + ], + "slots": [ + { + "name": "iconLeft", + "notes": "Optional leading icon, rendered at top-left inside the field." + }, + { + "name": "iconRight", + "notes": "Optional trailing icon, rendered at top-right. Suppressed when `disabled` (the lock icon takes over)." + } + ] + }, + "field-textarea": { + "import": "@aziontech/webkit/field-textarea", + "target": "./src/components/inputs/field-textarea/field-textarea.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/field-textarea", + "category": "inputs", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "string", + "default": "''", + "required": "no", + "doc": "Two-way bound value of the underlying Textarea." + }, + { + "name": "name", + "type": "string", + "default": "''", + "required": "no", + "doc": "HTML name for the underlying textarea (form integration)." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Text rendered inside the Label. When empty, the label row is omitted." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Placeholder forwarded to the Textarea." + }, + { + "name": "helperText", + "type": "string", + "default": "''", + "required": "no", + "doc": "Auxiliary text rendered inside HelperText. When empty, the helper row is omitted." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the textarea and switches the helper to `kind=disabled` (lock icon)." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the textarea read-only; value is visible but not editable. Native pass-through." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Adds the Required tag to the Label and sets native required + aria-required on the textarea." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Switches the helper to `kind=invalid` and applies invalid border tokens on the textarea." + }, + { + "name": "inputId", + "type": "string", + "default": "''", + "required": "no", + "doc": "id for the native textarea; consumed by Label via `for` and by `aria-describedby` wiring." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "string", + "notes": "v-model update." + } + ] + }, + "dropdown": { + "import": "@aziontech/webkit/dropdown", + "target": "./src/components/navigation/dropdown/index.ts", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown", + "category": "navigation", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state. Use with `v-model:open` or `@update:open`. When omitted, the component is uncontrolled." + }, + { + "name": "placement", + "type": "'auto' | 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'", + "default": "'bottom-start'", + "required": "no", + "doc": "Where the panel opens relative to the trigger. `'auto'` picks the best-fitting corner at open time based on viewport space." + }, + { + "name": "offset", + "type": "number", + "default": "4", + "required": "no", + "doc": "Pixel gap between the trigger and the panel." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Prevents the trigger from opening the panel and applies disabled tokens." + } + ], + "events": [ + { + "name": "update:open", + "payload": "boolean", + "notes": "Fires on every open/closed transition; supports `v-model:open`." + }, + { + "name": "select", + "payload": "{ value: string | number; event: MouseEvent | KeyboardEvent }", + "notes": "Fires when an enabled option is activated (click, `Enter`/`Space`, or a `command` shortcut). The panel closes automatically and focus returns to the trigger when activated from inside the open panel." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "top", + "notes": "—" + }, + { + "name": "bottom", + "notes": "—" + } + ] + }, + "dropdown-trigger": { + "import": "@aziontech/webkit/dropdown-trigger", + "target": "./src/components/navigation/dropdown/dropdown-trigger/dropdown-trigger.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-trigger", + "category": "navigation" + }, + "dropdown-group": { + "import": "@aziontech/webkit/dropdown-group", + "target": "./src/components/navigation/dropdown/dropdown-group/dropdown-group.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-group", + "category": "navigation" + }, + "dropdown-option": { + "import": "@aziontech/webkit/dropdown-option", + "target": "./src/components/navigation/dropdown/dropdown-option/dropdown-option.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-option", + "category": "navigation" + }, + "multi-select": { + "import": "@aziontech/webkit/multi-select", + "target": "./src/components/inputs/multi-select/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/multi-select-root", + "subcomponents": [ + "@aziontech/webkit/multi-select-trigger", + "@aziontech/webkit/multi-select-content", + "@aziontech/webkit/multi-select-group", + "@aziontech/webkit/multi-select-option" + ], + "category": "inputs", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "unknown[]", + "default": "() => []", + "required": "no", + "doc": "Two-way bound selection. Always an array; each item is the `value` of a selected ``." + }, + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state for the dropdown. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Trigger placeholder shown when no option is selected." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects trigger height (small=28px, medium=32px, large=40px) matching `InputText`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the trigger and prevents opening; applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; the current selection is visible but the dropdown cannot open." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets `aria-required` on the trigger. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid` on the trigger." + }, + { + "name": "displayValue", + "type": "(value: unknown[]) => string", + "default": "undefined", + "required": "no", + "doc": "Custom formatter used by the trigger to render the selected labels; receives the current `modelValue` array." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "unknown[]", + "notes": "Fires when an option is toggled; payload is the next selection array." + }, + { + "name": "update:open", + "payload": "boolean", + "notes": "Fires when the dropdown opens or closes." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "multi-select-root": { + "import": "@aziontech/webkit/multi-select-root", + "target": "./src/components/inputs/multi-select/multi-select.vue", + "kind": "root", + "rootOf": "multi-select", + "treeShakeableImport": "@aziontech/webkit/multi-select-root", + "category": "inputs", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "modelValue", + "type": "unknown[]", + "default": "() => []", + "required": "no", + "doc": "Two-way bound selection. Always an array; each item is the `value` of a selected ``." + }, + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state for the dropdown. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "placeholder", + "type": "string", + "default": "''", + "required": "no", + "doc": "Trigger placeholder shown when no option is selected." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects trigger height (small=28px, medium=32px, large=40px) matching `InputText`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the trigger and prevents opening; applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; the current selection is visible but the dropdown cannot open." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets `aria-required` on the trigger. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid` on the trigger." + }, + { + "name": "displayValue", + "type": "(value: unknown[]) => string", + "default": "undefined", + "required": "no", + "doc": "Custom formatter used by the trigger to render the selected labels; receives the current `modelValue` array." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "unknown[]", + "notes": "Fires when an option is toggled; payload is the next selection array." + }, + { + "name": "update:open", + "payload": "boolean", + "notes": "Fires when the dropdown opens or closes." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "multi-select-trigger": { + "import": "@aziontech/webkit/multi-select-trigger", + "target": "./src/components/inputs/multi-select/multi-select-trigger/multi-select-trigger.vue", + "kind": "subcomponent", + "parent": "multi-select", + "treeShakeableImport": "@aziontech/webkit/multi-select-trigger" + }, + "multi-select-content": { + "import": "@aziontech/webkit/multi-select-content", + "target": "./src/components/inputs/multi-select/multi-select-content/multi-select-content.vue", + "kind": "subcomponent", + "parent": "multi-select", + "treeShakeableImport": "@aziontech/webkit/multi-select-content" + }, + "multi-select-group": { + "import": "@aziontech/webkit/multi-select-group", + "target": "./src/components/inputs/multi-select/multi-select-group/multi-select-group.vue", + "kind": "subcomponent", + "parent": "multi-select", + "treeShakeableImport": "@aziontech/webkit/multi-select-group" + }, + "multi-select-option": { + "import": "@aziontech/webkit/multi-select-option", + "target": "./src/components/inputs/multi-select/multi-select-option/multi-select-option.vue", + "kind": "subcomponent", + "parent": "multi-select", + "treeShakeableImport": "@aziontech/webkit/multi-select-option" + }, + "select": { + "import": "@aziontech/webkit/select", + "target": "./src/components/inputs/select/index.ts", + "kind": "component", + "compoundRoot": true, + "treeShakeableImport": "@aziontech/webkit/select-root", + "subcomponents": [ + "@aziontech/webkit/select-trigger", + "@aziontech/webkit/select-content", + "@aziontech/webkit/select-group", + "@aziontech/webkit/select-option" + ], + "category": "inputs", + "structure": "composition", + "status": "draft", + "props": [ + { + "name": "modelValue", + "type": "string | number | unknown[]", + "default": "undefined", + "required": "no", + "doc": "Two-way bound selection. Scalar in single mode; array in multi mode." + }, + { + "name": "multiple", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, switches the component to multi-select; options render checkbox indicators and `modelValue` is an array." + }, + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state for the dropdown. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "placeholder", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Trigger placeholder shown when no option is selected." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects trigger height (small=28px, medium=32px, large=40px) matching `InputText`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the trigger and prevents opening; applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; the current selection is visible but the dropdown cannot open." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets `aria-required` on the trigger. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid` on the trigger." + }, + { + "name": "displayValue", + "type": "(value: unknown) => string", + "default": "undefined", + "required": "no", + "doc": "Custom formatter used by the trigger to render the selected label(s); receives the current `modelValue`." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "unknown", + "notes": "Fires when an option is selected (single) or toggled (multi)." + }, + { + "name": "update:open", + "payload": "boolean", + "notes": "Fires when the dropdown opens or closes." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "select-root": { + "import": "@aziontech/webkit/select-root", + "target": "./src/components/inputs/select/select.vue", + "kind": "root", + "rootOf": "select", + "treeShakeableImport": "@aziontech/webkit/select-root", + "category": "inputs", + "structure": "composition", + "status": "draft", + "props": [ + { + "name": "modelValue", + "type": "string | number | unknown[]", + "default": "undefined", + "required": "no", + "doc": "Two-way bound selection. Scalar in single mode; array in multi mode." + }, + { + "name": "multiple", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, switches the component to multi-select; options render checkbox indicators and `modelValue` is an array." + }, + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state for the dropdown. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "placeholder", + "type": "string", + "default": "undefined", + "required": "no", + "doc": "Trigger placeholder shown when no option is selected." + }, + { + "name": "size", + "type": "'small' | 'medium' | 'large'", + "default": "'medium'", + "required": "no", + "doc": "Size token; affects trigger height (small=28px, medium=32px, large=40px) matching `InputText`." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the trigger and prevents opening; applies disabled tokens." + }, + { + "name": "readonly", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field read-only; the current selection is visible but the dropdown cannot open." + }, + { + "name": "required", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the field as required; sets `aria-required` on the trigger. Visual indicator (asterisk) is owned by the wrapping form-field, not by this component." + }, + { + "name": "invalid", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Applies the invalid border + ring tokens and sets `aria-invalid` on the trigger." + }, + { + "name": "displayValue", + "type": "(value: unknown) => string", + "default": "undefined", + "required": "no", + "doc": "Custom formatter used by the trigger to render the selected label(s); receives the current `modelValue`." + } + ], + "events": [ + { + "name": "update:modelValue", + "payload": "unknown", + "notes": "Fires when an option is selected (single) or toggled (multi)." + }, + { + "name": "update:open", + "payload": "boolean", + "notes": "Fires when the dropdown opens or closes." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "select-trigger": { + "import": "@aziontech/webkit/select-trigger", + "target": "./src/components/inputs/select/select-trigger/select-trigger.vue", + "kind": "subcomponent", + "parent": "select", + "treeShakeableImport": "@aziontech/webkit/select-trigger" + }, + "select-content": { + "import": "@aziontech/webkit/select-content", + "target": "./src/components/inputs/select/select-content/select-content.vue", + "kind": "subcomponent", + "parent": "select", + "treeShakeableImport": "@aziontech/webkit/select-content" + }, + "select-group": { + "import": "@aziontech/webkit/select-group", + "target": "./src/components/inputs/select/select-group/select-group.vue", + "kind": "subcomponent", + "parent": "select", + "treeShakeableImport": "@aziontech/webkit/select-group" + }, + "select-option": { + "import": "@aziontech/webkit/select-option", + "target": "./src/components/inputs/select/select-option/select-option.vue", + "kind": "subcomponent", + "parent": "select", + "treeShakeableImport": "@aziontech/webkit/select-option" + }, + "divider": { + "import": "@aziontech/webkit/divider", + "target": "./src/components/layout/divider/divider.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/divider", + "category": "layout", + "structure": "monolithic", + "status": "approved", + "props": [ + { + "name": "orientation", + "type": "'horizontal' | 'vertical'", + "default": "'horizontal'", + "required": "no", + "doc": "Layout axis of the separator line." + }, + { + "name": "label", + "type": "string", + "default": "''", + "required": "no", + "doc": "Fallback centered text shown when the default slot is empty." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "scroll-area": { + "import": "@aziontech/webkit/scroll-area", + "target": "./src/components/layout/scroll-area/scroll-area.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/scroll-area", + "category": "layout", + "structure": "monolithic", + "status": "implemented", + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "global-header": { + "import": "@aziontech/webkit/global-header", + "target": "./src/components/layout/global-header/index.js", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/global-header", + "category": "layout", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "ariaLabel", + "type": "string", + "default": "'Global header'", + "required": "no", + "doc": "Accessible name for the header landmark." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + } + ] + }, + "sidebar": { + "import": "@aziontech/webkit/sidebar", + "target": "./src/components/layout/sidebar/sidebar.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/sidebar", + "category": "layout", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "ariaLabel", + "type": "string", + "default": "'Sidebar'", + "required": "no", + "doc": "Accessible name for the navigation landmark." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "header", + "notes": "—" + }, + { + "name": "footer", + "notes": "—" + } + ] + }, + "sidebar-group": { + "import": "@aziontech/webkit/sidebar-group", + "target": "./src/components/layout/sidebar/sidebar-group.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/sidebar-group", + "category": "layout" + }, + "sidebar-header": { + "import": "@aziontech/webkit/sidebar-header", + "target": "./src/components/layout/sidebar/sidebar-header.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/sidebar-header", + "category": "layout" + }, + "sidebar-footer": { + "import": "@aziontech/webkit/sidebar-footer", + "target": "./src/components/layout/sidebar/sidebar-footer.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/sidebar-footer", + "category": "layout" + }, + "breadcrumb": { + "import": "@aziontech/webkit/breadcrumb", + "target": "./src/components/navigation/breadcrumb/index.js", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/breadcrumb", + "category": "navigation", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "items", + "type": "BreadcrumbItemData[]", + "default": "() => []", + "required": "no", + "doc": "Ordered path segments; the last entry is treated as the current page when `current` is omitted on that entry." + } + ], + "events": [ + { + "name": "navigate", + "payload": "href: string", + "notes": "Fires when an ancestor segment is activated." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "breadcrumb-item": { + "import": "@aziontech/webkit/breadcrumb-item", + "target": "./src/components/navigation/breadcrumb-item/breadcrumb-item.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/breadcrumb-item", + "category": "navigation", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "label", + "type": "string", + "default": "'Page Name'", + "required": "no", + "doc": "Visible segment label." + }, + { + "name": "href", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "Destination when the segment is a link (ignored when `current` is true)." + }, + { + "name": "current", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Marks the current page (last segment); renders as span with `aria-current=\"page\"`." + }, + { + "name": "showIcon", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "When true, renders a leading PrimeIcons icon before the label." + }, + { + "name": "icon", + "type": "string", + "default": "'pi pi-box'", + "required": "no", + "doc": "PrimeIcons class for the leading icon when `showIcon` is true." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "Fires when the segment is activated (link or button)." + } + ] + }, + "link": { + "import": "@aziontech/webkit/link", + "target": "./src/components/navigation/link/link.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/link", + "category": "navigation", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "label", + "type": "string", + "default": "'Learn More'", + "required": "no", + "doc": "Visible label rendered inside the link." + }, + { + "name": "size", + "type": "'large' | 'medium'", + "default": "'large'", + "required": "no", + "doc": "Size token; affects height, gap, and typography." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables interaction and applies disabled tokens." + }, + { + "name": "showIcon", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, renders the trailing icon." + }, + { + "name": "icon", + "type": "string", + "default": "'pi pi-external-link'", + "required": "no", + "doc": "PrimeIcons class for the trailing icon." + }, + { + "name": "href", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "Destination URL for the anchor." + }, + { + "name": "target", + "type": "'_blank' | '_self'", + "default": "'_self'", + "required": "no", + "doc": "Link target when navigating." + } + ], + "events": [ + { + "name": "click", + "payload": "MouseEvent", + "notes": "—" + } + ] + }, + "menu-item": { + "import": "@aziontech/webkit/menu-item", + "target": "./src/components/navigation/menu-item/menu-item.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/menu-item", + "category": "navigation", + "structure": "monolithic", + "status": "implemented", + "events": [ + { + "name": "click", + "payload": "event: MouseEvent", + "notes": "—" + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + }, + { + "name": "tag", + "notes": "—" + } + ] + }, + "tab-view": { + "import": "@aziontech/webkit/tab-view", + "target": "./src/components/navigation/tab-view/index.js", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/tab-view", + "category": "navigation", + "structure": "composition", + "status": "implemented", + "events": [ + { + "name": "update:value", + "payload": "value: TabViewValue | null", + "notes": "v-model:value." + }, + { + "name": "value-change", + "payload": "value: TabViewValue | null", + "notes": "—" + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "navigation-menu": { + "import": "@aziontech/webkit/navigation-menu", + "target": "./src/components/navigation/navigation-menu/index.js", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/navigation-menu", + "category": "navigation", + "structure": "composition", + "status": "implemented", + "events": [ + { + "name": "update:value", + "payload": "value: NavigationMenuValue, eventDetails: unknown", + "notes": "v-model:value." + }, + { + "name": "value-change", + "payload": "value: NavigationMenuValue, eventDetails: unknown", + "notes": "—" + }, + { + "name": "open-change-complete", + "payload": "open: boolean", + "notes": "—" + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "tooltip": { + "import": "@aziontech/webkit/tooltip", + "target": "./src/components/overlay/tooltip/tooltip.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/tooltip", + "category": "overlay", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "text", + "type": "string", + "default": "''", + "required": "yes", + "doc": "Plain text shown inside the tooltip." + }, + { + "name": "placement", + "type": "'top' | 'right' | 'bottom' | 'left'", + "default": "'top'", + "required": "no", + "doc": "Anchor side relative to the trigger." + }, + { + "name": "delay", + "type": "number", + "default": "200", + "required": "no", + "doc": "Hover-open delay in milliseconds." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables tooltip activation." + }, + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Initial open state when uncontrolled." + } + ], + "events": [ + { + "name": "update:open", + "payload": "boolean", + "notes": "v-model:open. Fires on open and close." + }, + { + "name": "show", + "payload": "void", + "notes": "Emitted right after the open transition starts." + }, + { + "name": "hide", + "payload": "void", + "notes": "Emitted right after the close transition starts." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "dialog": { + "import": "@aziontech/webkit/dialog", + "target": "./src/components/overlay/dialog/dialog.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog", + "category": "overlay", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "closeable", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "When true, overlay click and Escape close the dialog." + }, + { + "name": "size", + "type": "DialogSize", + "default": "undefined", + "required": "no", + "doc": "Panel max-width preset passed to the inner Panel." + } + ], + "events": [ + { + "name": "update:open", + "payload": "value: boolean", + "notes": "v-model:open." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "dialog-close": { + "import": "@aziontech/webkit/dialog-close", + "target": "./src/components/overlay/dialog/dialog-close.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-close", + "category": "overlay" + }, + "dialog-content": { + "import": "@aziontech/webkit/dialog-content", + "target": "./src/components/overlay/dialog/dialog-content.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-content", + "category": "overlay" + }, + "dialog-description": { + "import": "@aziontech/webkit/dialog-description", + "target": "./src/components/overlay/dialog/dialog-description.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-description", + "category": "overlay" + }, + "dialog-overlay": { + "import": "@aziontech/webkit/dialog-overlay", + "target": "./src/components/overlay/dialog/dialog-overlay.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-overlay", + "category": "overlay" + }, + "dialog-portal": { + "import": "@aziontech/webkit/dialog-portal", + "target": "./src/components/overlay/dialog/dialog-portal.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-portal", + "category": "overlay" + }, + "dialog-title": { + "import": "@aziontech/webkit/dialog-title", + "target": "./src/components/overlay/dialog/dialog-title.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-title", + "category": "overlay" + }, + "dialog-trigger": { + "import": "@aziontech/webkit/dialog-trigger", + "target": "./src/components/overlay/dialog/dialog-trigger.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dialog-trigger", + "category": "overlay" + }, + "drawer": { + "import": "@aziontech/webkit/drawer", + "target": "./src/components/overlay/drawer/drawer.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer", + "category": "overlay", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "open", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Controlled open state. Use with `v-model:open`." + }, + { + "name": "defaultOpen", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "Initial open state when uncontrolled." + }, + { + "name": "closeable", + "type": "boolean", + "default": "undefined", + "required": "no", + "doc": "When true, overlay click and Escape close the drawer." + }, + { + "name": "side", + "type": "DrawerSide", + "default": "undefined", + "required": "no", + "doc": "Edge the drawer panel slides from." + }, + { + "name": "size", + "type": "DrawerSize", + "default": "undefined", + "required": "no", + "doc": "Panel max-width preset (`small` 384px, `medium` 672px, `large` 1024px). Height is always 100% viewport." + } + ], + "events": [ + { + "name": "update:open", + "payload": "value: boolean", + "notes": "v-model:open." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "drawer-close": { + "import": "@aziontech/webkit/drawer-close", + "target": "./src/components/overlay/drawer/drawer-close.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-close", + "category": "overlay" + }, + "drawer-content": { + "import": "@aziontech/webkit/drawer-content", + "target": "./src/components/overlay/drawer/drawer-content.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-content", + "category": "overlay" + }, + "drawer-description": { + "import": "@aziontech/webkit/drawer-description", + "target": "./src/components/overlay/drawer/drawer-description.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-description", + "category": "overlay" + }, + "drawer-overlay": { + "import": "@aziontech/webkit/drawer-overlay", + "target": "./src/components/overlay/drawer/drawer-overlay.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-overlay", + "category": "overlay" + }, + "drawer-portal": { + "import": "@aziontech/webkit/drawer-portal", + "target": "./src/components/overlay/drawer/drawer-portal.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-portal", + "category": "overlay" + }, + "drawer-title": { + "import": "@aziontech/webkit/drawer-title", + "target": "./src/components/overlay/drawer/drawer-title.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-title", + "category": "overlay" + }, + "drawer-trigger": { + "import": "@aziontech/webkit/drawer-trigger", + "target": "./src/components/overlay/drawer/drawer-trigger.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/drawer-trigger", + "category": "overlay" + }, + "panel": { + "import": "@aziontech/webkit/panel", + "target": "./src/components/overlay/panel/panel.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/panel", + "category": "overlay", + "structure": "composition", + "status": "implemented", + "props": [ + { + "name": "size", + "type": "PanelSize", + "default": "undefined", + "required": "no", + "doc": "Max width of the panel shell." + } + ], + "slots": [ + { + "name": "default", + "notes": "—" + } + ] + }, + "panel-content": { + "import": "@aziontech/webkit/panel-content", + "target": "./src/components/overlay/panel/panel-content.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/panel-content", + "category": "overlay" + }, + "panel-footer": { + "import": "@aziontech/webkit/panel-footer", + "target": "./src/components/overlay/panel/panel-footer.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/panel-footer", + "category": "overlay" + }, + "panel-header": { + "import": "@aziontech/webkit/panel-header", + "target": "./src/components/overlay/panel/panel-header.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/panel-header", + "category": "overlay" + }, + "dropdown-menu": { + "import": "@aziontech/webkit/dropdown-menu", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu", + "category": "overlay" + }, + "dropdown-menu-trigger": { + "import": "@aziontech/webkit/dropdown-menu-trigger", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-trigger.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-trigger", + "category": "overlay" + }, + "dropdown-menu-portal": { + "import": "@aziontech/webkit/dropdown-menu-portal", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-portal.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-portal", + "category": "overlay" + }, + "dropdown-menu-content": { + "import": "@aziontech/webkit/dropdown-menu-content", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-content.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-content", + "category": "overlay" + }, + "dropdown-menu-group": { + "import": "@aziontech/webkit/dropdown-menu-group", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-group.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-group", + "category": "overlay" + }, + "dropdown-menu-item": { + "import": "@aziontech/webkit/dropdown-menu-item", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-item.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-item", + "category": "overlay" + }, + "dropdown-menu-separator": { + "import": "@aziontech/webkit/dropdown-menu-separator", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-separator.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-separator", + "category": "overlay" + }, + "dropdown-menu-from-model": { + "import": "@aziontech/webkit/dropdown-menu-from-model", + "target": "./src/components/overlay/dropdown-menu/dropdown-menu-from-model.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-from-model", + "category": "overlay" + }, + "dropdown-menu-factory": { + "import": "@aziontech/webkit/dropdown-menu-factory", + "target": "./src/components/overlay/dropdown-menu/factory.ts", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/dropdown-menu-factory", + "category": "overlay" + }, + "deploy-success": { + "import": "@aziontech/webkit/deploy-success", + "target": "./src/components/templates/deploy-success/deploy-success.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/deploy-success", + "category": "templates", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "title", + "type": "string", + "default": "'Congratulations!'", + "required": "no", + "doc": "Main success heading in the card header region." + }, + { + "name": "description", + "type": "string", + "default": "'You just deployed a new application into'", + "required": "no", + "doc": "Supporting copy before the scope tag." + }, + { + "name": "scope", + "type": "string", + "default": "'mygithub-scope'", + "required": "no", + "doc": "Scope label rendered in the header tag." + }, + { + "name": "appName", + "type": "string", + "default": "'myappname'", + "required": "no", + "doc": "Deployed application name above the build log." + }, + { + "name": "lines", + "type": "LogViewLine[]", + "default": "[]", + "required": "no", + "doc": "Build log entries passed to LogView." + }, + { + "name": "stepsLabel", + "type": "string", + "default": "'Next Steps'", + "required": "no", + "doc": "Section label above the next-steps list." + }, + { + "name": "steps", + "type": "DeploySuccessStep[]", + "default": "[]", + "required": "yes", + "doc": "Ordered next-step entries (title, description, icon per row)." + }, + { + "name": "actionLabel", + "type": "string", + "default": "'Manage'", + "required": "no", + "doc": "Primary footer button label." + }, + { + "name": "visitLabel", + "type": "string", + "default": "'Visit'", + "required": "no", + "doc": "Visit link label in the card header." + }, + { + "name": "visitHref", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "Visit link destination URL." + }, + { + "name": "showHeader", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, renders the top GlobalHeader bar with the brand slot." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables toolbar controls and the primary action button." + } + ], + "events": [ + { + "name": "action-click", + "payload": "MouseEvent", + "notes": "Fires when the primary footer button is activated." + }, + { + "name": "visit-click", + "payload": "MouseEvent", + "notes": "Fires when the visit link is activated." + }, + { + "name": "step-click", + "payload": "[index: number, event: MouseEvent]", + "notes": "Fires when a next-step row is activated." + } + ], + "slots": [ + { + "name": "header", + "notes": "—" + }, + { + "name": "success", + "notes": "—" + }, + { + "name": "logs", + "notes": "—" + }, + { + "name": "steps", + "notes": "—" + }, + { + "name": "actions", + "notes": "—" + } + ] + }, + "onboarding-form": { + "import": "@aziontech/webkit/onboarding-form", + "target": "./src/components/templates/onboarding-form/onboarding-form.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/onboarding-form", + "category": "templates", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "heading", + "type": "string", + "default": "'Your first global deployment is seconds away'", + "required": "no", + "doc": "Page title above the form sections." + }, + { + "name": "planLabel", + "type": "string", + "default": "'Plan Selected'", + "required": "no", + "doc": "Label above the plan summary card." + }, + { + "name": "planTitle", + "type": "string", + "default": "'Pro Plan'", + "required": "no", + "doc": "Selected plan name in the summary card." + }, + { + "name": "planPrice", + "type": "string", + "default": "'$20/MO'", + "required": "no", + "doc": "Price badge text beside the plan title." + }, + { + "name": "planDescription", + "type": "string", + "default": "'For professional or commercial workloads.'", + "required": "no", + "doc": "Supporting copy under the plan title." + }, + { + "name": "changeLabel", + "type": "string", + "default": "'CHANGE'", + "required": "no", + "doc": "Label for the plan change action button." + }, + { + "name": "usageLabel", + "type": "string", + "default": "'How are you planning to use Azion?'", + "required": "no", + "doc": "Label for the usage intent grid." + }, + { + "name": "usageRequired", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, appends a required indicator to the usage label." + }, + { + "name": "usageValue", + "type": "string | number | undefined", + "default": "undefined", + "required": "no", + "doc": "Selected usage option (v-model)." + }, + { + "name": "usageItems", + "type": "BoxGridSelectionItem[]", + "default": "undefined", + "required": "yes", + "doc": "Options for `BoxGridSelection`." + }, + { + "name": "fullNameLabel", + "type": "string", + "default": "'Your Full Name'", + "required": "no", + "doc": "Label for the full name field." + }, + { + "name": "fullNameRequired", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, appends a required indicator to the full name label." + }, + { + "name": "fullName", + "type": "string", + "default": "''", + "required": "no", + "doc": "Full name value (v-model)." + }, + { + "name": "fullNamePlaceholder", + "type": "string", + "default": "'John Doe'", + "required": "no", + "doc": "Placeholder for the full name input." + }, + { + "name": "scheduleLabel", + "type": "string", + "default": "'Schedule an onboarding session with an Azion expert'", + "required": "no", + "doc": "Label for the scheduling checkbox." + }, + { + "name": "scheduleOnboarding", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "Whether the scheduling checkbox is checked (v-model)." + }, + { + "name": "continueLabel", + "type": "string", + "default": "'CONTINUE'", + "required": "no", + "doc": "Primary submit button label." + }, + { + "name": "continueDisabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the continue button." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables all interactive fields in the template." + }, + { + "name": "footerPrefix", + "type": "string", + "default": "'Have enterprise requirements?'", + "required": "no", + "doc": "Footer prompt before the contact link." + }, + { + "name": "footerLinkLabel", + "type": "string", + "default": "'Get in touch'", + "required": "no", + "doc": "Footer contact link label." + }, + { + "name": "footerLinkHref", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "Footer contact link URL." + } + ], + "events": [ + { + "name": "update:usageValue", + "payload": "string | number", + "notes": "v-model for usage selection." + }, + { + "name": "update:fullName", + "payload": "string", + "notes": "v-model for full name." + }, + { + "name": "update:scheduleOnboarding", + "payload": "boolean", + "notes": "v-model for scheduling checkbox." + }, + { + "name": "change-click", + "payload": "MouseEvent", + "notes": "Fires when the plan change button is activated." + }, + { + "name": "continue-click", + "payload": "MouseEvent", + "notes": "Fires when the continue button is activated." + }, + { + "name": "footer-link-click", + "payload": "MouseEvent", + "notes": "Fires when the footer contact link is activated." + } + ], + "slots": [ + { + "name": "plan", + "notes": "—" + }, + { + "name": "footer", + "notes": "—" + } + ] + }, + "plan-success": { + "import": "@aziontech/webkit/plan-success", + "target": "./src/components/templates/plan-success/plan-success.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/plan-success", + "category": "templates", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "title", + "type": "string", + "default": "'Your Pro Plan is now Active'", + "required": "no", + "doc": "Main success heading in the card header region." + }, + { + "name": "description", + "type": "string", + "default": "'A receipt has been sent to your email for your records.'", + "required": "no", + "doc": "Supporting copy under the success heading." + }, + { + "name": "stepsLabel", + "type": "string", + "default": "'Next Steps'", + "required": "no", + "doc": "Section label above the numbered steps list." + }, + { + "name": "steps", + "type": "PlanSuccessStep[]", + "default": "undefined", + "required": "yes", + "doc": "Ordered next-step entries (title + description per row)." + }, + { + "name": "actionLabel", + "type": "string", + "default": "'Start deploying'", + "required": "no", + "doc": "Primary footer button label." + }, + { + "name": "showHeader", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "When true, renders the top `GlobalHeader` bar with the brand slot." + }, + { + "name": "disabled", + "type": "boolean", + "default": "false", + "required": "no", + "doc": "Disables the primary action button." + } + ], + "events": [ + { + "name": "action-click", + "payload": "MouseEvent", + "notes": "Fires when the primary footer button is activated." + } + ], + "slots": [ + { + "name": "header", + "notes": "—" + }, + { + "name": "success", + "notes": "—" + }, + { + "name": "steps", + "notes": "—" + }, + { + "name": "actions", + "notes": "—" + } + ] + }, + "platform-shell": { + "import": "@aziontech/webkit/platform-shell", + "target": "./src/components/templates/platform-shell/platform-shell.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/platform-shell", + "category": "templates", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "headerAriaLabel", + "type": "string", + "default": "'Global header'", + "required": "no", + "doc": "Accessible name for the header landmark." + }, + { + "name": "tabDefaultValue", + "type": "string | number | null", + "default": "'tab-1'", + "required": "no", + "doc": "Initial active tab when the `tabs` slot is used (uncontrolled)." + } + ], + "slots": [ + { + "name": "header-left", + "notes": "—" + }, + { + "name": "header-middle", + "notes": "—" + }, + { + "name": "header-right", + "notes": "—" + }, + { + "name": "sidebar", + "notes": "—" + }, + { + "name": "page-header", + "notes": "—" + }, + { + "name": "tabs", + "notes": "—" + }, + { + "name": "default", + "notes": "—" + } + ] + }, + "sign-up-card": { + "import": "@aziontech/webkit/sign-up-card", + "target": "./src/components/templates/sign-up-card/sign-up-card.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/sign-up-card", + "category": "templates", + "structure": "monolithic", + "status": "implemented", + "props": [ + { + "name": "title", + "type": "string", + "default": "'Sign Up for a Free Account'", + "required": "no", + "doc": "title." + }, + { + "name": "subtitle", + "type": "string", + "default": "'US$ 300 credit to use over 12 months", + "required": "no", + "doc": "subtitle." + }, + { + "name": "githubLabel", + "type": "string", + "default": "'Continue with Github'", + "required": "no", + "doc": "github Label." + }, + { + "name": "googleLabel", + "type": "string", + "default": "'Continue with Google'", + "required": "no", + "doc": "google Label." + }, + { + "name": "emailLabel", + "type": "string", + "default": "'Sign Up with Work Email'", + "required": "no", + "doc": "email Label." + }, + { + "name": "signInPrompt", + "type": "string", + "default": "'Already have an account?'", + "required": "no", + "doc": "sign In Prompt." + }, + { + "name": "signInLabel", + "type": "string", + "default": "'Sign in'", + "required": "no", + "doc": "sign In Label." + }, + { + "name": "termsHref", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "terms Href." + }, + { + "name": "privacyHref", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "privacy Href." + }, + { + "name": "signInHref", + "type": "string", + "default": "'#'", + "required": "no", + "doc": "sign In Href." + }, + { + "name": "showGithub", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "show Github." + }, + { + "name": "showGoogle", + "type": "boolean", + "default": "true", + "required": "no", + "doc": "show Google." + } + ], + "events": [ + { + "name": "github-click", + "payload": "unknown", + "notes": "—" + }, + { + "name": "google-click", + "payload": "unknown", + "notes": "—" + }, + { + "name": "email-click", + "payload": "unknown", + "notes": "—" + }, + { + "name": "sign-in-click", + "payload": "unknown", + "notes": "—" + }, + { + "name": "terms-click", + "payload": "unknown", + "notes": "—" + }, + { + "name": "privacy-click", + "payload": "unknown", + "notes": "—" + } + ] + }, + "spinner": { + "import": "@aziontech/webkit/spinner", + "target": "./src/components/utils/spinner/spinner.vue", + "kind": "component", + "treeShakeableImport": "@aziontech/webkit/spinner", + "category": "utils", + "structure": "monolithic", + "status": "implemented" + }, + "svg/error-403": { + "import": "@aziontech/webkit/svg/error-403", + "target": "./src/svg/error-403/error-403.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/error-403" + }, + "svg/error-404": { + "import": "@aziontech/webkit/svg/error-404", + "target": "./src/svg/error-404/error-404.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/error-404" + }, + "svg/azion/full": { + "import": "@aziontech/webkit/svg/azion/full", + "target": "./src/svg/azion/full/full.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/azion/full" + }, + "svg/azion/move-to-the-edge": { + "import": "@aziontech/webkit/svg/azion/move-to-the-edge", + "target": "./src/svg/azion/move-to-the-edge/move-to-the-edge.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/azion/move-to-the-edge" + }, + "svg/azion/technologies": { + "import": "@aziontech/webkit/svg/azion/technologies", + "target": "./src/svg/azion/technologies/technologies.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/azion/technologies" + }, + "svg/azion/default": { + "import": "@aziontech/webkit/svg/azion/default", + "target": "./src/svg/azion/default/default.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/azion/default" + }, + "svg/azion/min": { + "import": "@aziontech/webkit/svg/azion/min", + "target": "./src/svg/azion/min/min.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/azion/min" + }, + "svg/illustration-layers": { + "import": "@aziontech/webkit/svg/illustration-layers", + "target": "./src/svg/illustration-layers/illustration-layers.vue", + "kind": "svg", + "treeShakeableImport": "@aziontech/webkit/svg/illustration-layers" + }, + "utils/cn": { + "import": "@aziontech/webkit/utils/cn", + "target": "./src/utils/cn.js", + "kind": "util", + "treeShakeableImport": "@aziontech/webkit/utils/cn" + }, + "styles/country-flags": { + "import": "@aziontech/webkit/styles/country-flags", + "target": "./src/styles/country-flags.css", + "kind": "style", + "treeShakeableImport": "@aziontech/webkit/styles/country-flags" + }, + "use-controllable": { + "import": "@aziontech/webkit/use-controllable", + "target": "./src/composables/use-controllable/index.js", + "kind": "composable", + "treeShakeableImport": "@aziontech/webkit/use-controllable" + }, + "use-focus-trap": { + "import": "@aziontech/webkit/use-focus-trap", + "target": "./src/composables/use-focus-trap/index.js", + "kind": "composable", + "treeShakeableImport": "@aziontech/webkit/use-focus-trap" + }, + "use-placement": { + "import": "@aziontech/webkit/use-placement", + "target": "./src/composables/use-placement/index.js", + "kind": "composable", + "treeShakeableImport": "@aziontech/webkit/use-placement" + }, + "vite": { + "import": "@aziontech/webkit/vite", + "target": "./src/vite/index.js", + "kind": "other", + "treeShakeableImport": "@aziontech/webkit/vite" + } + } +} diff --git a/packages/webkit/package.json b/packages/webkit/package.json index c275c5a9e..bc9133544 100644 --- a/packages/webkit/package.json +++ b/packages/webkit/package.json @@ -23,6 +23,8 @@ "lint:style:fix": "stylelint \"src/**/*.{css,scss,vue}\" --fix", "type-check": "vue-tsc --noEmit", "type-coverage": "type-coverage -p . --at-least 95 --detail", + "catalog:build": "node scripts/build-catalog.mjs", + "catalog:check": "node scripts/build-catalog.mjs && git diff --exit-code --stat catalog.json", "figma:parse": "figma connect parse", "figma:publish": "figma connect publish", "figma:unpublish": "figma connect unpublish" @@ -50,6 +52,7 @@ }, "files": [ "src", + "catalog.json", "package.json" ], "sideEffects": [ @@ -57,6 +60,8 @@ "**/*.css" ], "exports": { + "./catalog.json": "./catalog.json", + "./package.json": "./package.json", "./empty-results-block": "./src/components/empty-results-block/empty-results-block.vue", "./overline": "./src/components/overline/overline.vue", "./button": "./src/components/actions/button/button.vue", diff --git a/packages/webkit/scripts/build-catalog.mjs b/packages/webkit/scripts/build-catalog.mjs new file mode 100644 index 000000000..69065da9b --- /dev/null +++ b/packages/webkit/scripts/build-catalog.mjs @@ -0,0 +1,225 @@ +#!/usr/bin/env node +// Generates packages/webkit/catalog.json — the machine-readable, version-locked +// manifest that the adoption tooling reads (eslint-plugin-webkit, webkit-mcp, +// the Claude Code bundle). One source of truth, derived from: +// - packages/webkit/package.json#exports (the canonical allowed-import list) +// - .specs/*.md (per-component API: props/events/slots) +// +// Deterministic by design (no timestamps) so a CI drift-check can assert the +// committed catalog.json matches a fresh build. Re-run at release time via the +// .releaserc `prepareCmd` so it always tracks the published exports. +// +// No external deps — Node built-ins + the repo's own spec parser. + +import { readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +import { parseSpecFile, getSection } from '../../../.claude/hooks/_lib/spec.mjs' + +const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)) +const REPO_ROOT = resolve(SCRIPT_DIR, '../../..') +const PKG_PATH = resolve(REPO_ROOT, 'packages/webkit/package.json') +const SPECS_DIR = resolve(REPO_ROOT, '.specs') +const OUT_PATH = resolve(REPO_ROOT, 'packages/webkit/catalog.json') + +const PKG_NAME = '@aziontech/webkit' + +// Token rules ported verbatim from .claude/hooks/validate-tokens.mjs so the +// linter / stylelint / MCP validate against the same set the DS itself enforces. +// Stored as source strings + flags (JSON-serializable) — consumers rebuild RegExp. +const TOKEN_RULES = [ + { id: 'hex-color', pattern: '#[0-9a-fA-F]{3,8}\\b', flags: 'g', message: 'Hex color hardcoded. Use semantic tokens (var(--primary), var(--bg-surface), var(--text-default), ...).' }, + { id: 'rgb-hsl', pattern: '\\b(rgba?|hsla?)\\s*\\(', flags: 'g', message: 'RGB/HSL hardcoded. Use semantic tokens via var(--*).' }, + { id: 'tailwind-palette', pattern: '\\b(?:bg|text|border|ring|outline|fill|stroke|divide|placeholder|caret|accent)-(?:gray|slate|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-\\d{2,3}\\b', flags: 'g', message: 'Tailwind palette color. Use semantic webkit tokens (var(--primary), var(--text-default), var(--bg-surface), ...).' }, + { id: 'typography-raw-size', pattern: '\\btext-(?:xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)\\b(?!-)', flags: 'g', message: 'Raw Tailwind text size. Use a generated typography class (text-heading-md, text-body-sm, text-button-lg, ...).' }, + { id: 'primevue-color', pattern: '(? c.trim().replace(/\s*\\$/, '')) +} + +/** + * Parse a markdown table into header-keyed rows, tolerating union types like + * `'a' | 'b' | 'c'` inside a cell (unescaped `|`). Extra cells are absorbed by + * `overflowHeader`'s column (joined with ' | '); the trailing columns keep their + * positions. Returns [] when the section has no table. + */ +function parseSpecTable(section, overflowHeader) { + if (!section) return [] + const lines = section.split('\n').map((l) => l.trim()).filter((l) => l.startsWith('|')) + if (lines.length < 2) return [] + const headers = splitRow(lines[0]).map((h) => h.toLowerCase()) + const n = headers.length + const oIdx = headers.indexOf(overflowHeader) + const rows = [] + for (let i = 2; i < lines.length; i++) { + let cells = splitRow(lines[i]) + if (cells.every((c) => !c)) continue + if (cells.length > n && oIdx >= 0) { + const tailCount = n - 1 - oIdx + const before = cells.slice(0, oIdx) + const tail = tailCount > 0 ? cells.slice(cells.length - tailCount) : [] + const mid = cells.slice(oIdx, cells.length - tailCount).join(' | ') + cells = [...before, mid, ...tail] + } + const row = {} + headers.forEach((h, idx) => { row[h] = (cells[idx] ?? '').trim() }) + rows.push(row) + } + return rows +} + +/** Category from an export target path: src/components///... → . */ +function categoryFromTarget(target) { + const m = target.match(/^\.\/src\/components\/([^/]+)\/([^/]+)\//) + if (!m) return null + // Two shapes: components/// (3 dirs) or components// (name only). + // When the 2nd segment is the component's own file dir (name === 2nd seg name), the + // 1st segment is the category. When there is no category dir the regex still yields + // as [1]; guard by requiring the target to have the deeper shape. + return m[1] +} + +/** kind by target path for non-component exports. */ +function kindFromTarget(target) { + if (target.startsWith('./src/utils/')) return 'util' + if (target.startsWith('./src/composables/')) return 'composable' + if (target.startsWith('./src/svg/')) return 'svg' + if (target.startsWith('./src/styles/')) return 'style' + if (target.startsWith('./src/vite/')) return 'other' + return 'component' +} + +function loadSpec(subpath) { + const specFile = resolve(SPECS_DIR, `${subpath}.md`) + if (!existsSync(specFile)) return null + const { frontmatter, body } = parseSpecFile(specFile) + if (!frontmatter) return null + + const props = parseSpecTable(getSection(body, 'Props') || '', 'type') + .map((r) => ({ name: clean(r.prop), type: clean(r.type), default: clean(r.default), required: clean(r.required), doc: clean(r.jsdoc) })) + .filter((p) => p.name && p.name !== '_none_') + const events = parseSpecTable(getSection(body, 'Events') || '', 'payload') + .map((r) => ({ name: clean(r.event), payload: clean(r.payload), notes: clean(r.notes) })) + .filter((e) => e.name && e.name !== '_none_') + const slots = parseSpecTable(getSection(body, 'Slots') || '', 'notes') + .map((r) => ({ name: clean(Object.values(r)[0]), notes: clean(Object.values(r)[1]) })) + .filter((s) => s.name && s.name !== '_none_') + + return { + category: frontmatter.category ?? null, + structure: frontmatter.structure ?? null, + status: frontmatter.status ?? null, + props, + events, + slots + } +} + +function build() { + const pkg = JSON.parse(readFileSync(PKG_PATH, 'utf-8')) + const exportsMap = pkg.exports || {} + const subpaths = Object.keys(exportsMap) + .filter((k) => k.startsWith('./')) + .map((k) => k.slice(2)) + const subpathSet = new Set(subpaths) + + // Compound roots: `X` is compound iff `X-root` is also exported. + const compoundRoots = subpaths.filter((s) => !s.includes('/') && subpathSet.has(`${s}-root`)) + // Longest-first so multi-select wins over select when attributing subcomponents. + const compoundByLength = [...compoundRoots].sort((a, b) => b.length - a.length) + + function parentCompound(subpath) { + if (subpath.endsWith('-root')) return null + for (const c of compoundByLength) { + if (subpath !== c && subpath.startsWith(`${c}-`)) return c + } + return null + } + + const imports = {} + for (const subpath of subpaths) { + const target = exportsMap[`./${subpath}`] + const importPath = `${PKG_NAME}/${subpath}` + const entry = { import: importPath, target } + + // Non-module exports (package.json, catalog.json): valid imports, not components. + if (subpath.endsWith('.json')) { + entry.kind = 'other' + entry.treeShakeableImport = importPath + imports[subpath] = entry + continue + } + + const isCompound = compoundRoots.includes(subpath) + const isRoot = subpath.endsWith('-root') && subpathSet.has(subpath.slice(0, -'-root'.length)) + const parent = parentCompound(subpath) + + if (isRoot) { + entry.kind = 'root' + entry.rootOf = subpath.slice(0, -'-root'.length) + entry.treeShakeableImport = importPath + } else if (parent) { + entry.kind = 'subcomponent' + entry.parent = parent + entry.treeShakeableImport = importPath + } else if (subpath.includes('/')) { + const seg = subpath.split('/')[0] + entry.kind = seg === 'svg' ? 'svg' : seg === 'utils' ? 'util' : seg === 'styles' ? 'style' : 'component' + entry.treeShakeableImport = importPath + } else { + entry.kind = kindFromTarget(target) + if (isCompound) { + entry.compoundRoot = true + entry.treeShakeableImport = `${PKG_NAME}/${subpath}-root` + entry.subcomponents = subpaths + .filter((s) => s !== `${subpath}-root` && s !== subpath && s.startsWith(`${subpath}-`) && parentCompound(s) === subpath) + .map((s) => `${PKG_NAME}/${s}`) + } else { + entry.treeShakeableImport = importPath + } + } + + // Enrich components/roots with spec API when a spec exists. + const spec = loadSpec(subpath) || (entry.kind === 'root' ? loadSpec(entry.rootOf) : null) + if (spec) { + entry.category = spec.category ?? categoryFromTarget(target) + entry.structure = spec.structure + entry.status = spec.status + if (spec.props.length) entry.props = spec.props + if (spec.events.length) entry.events = spec.events + if (spec.slots.length) entry.slots = spec.slots + } else if (entry.kind === 'component') { + entry.category = categoryFromTarget(target) + } + + imports[subpath] = entry + } + + return { + package: PKG_NAME, + webkitVersion: pkg.version, + deniedPrefixes: [`${PKG_NAME}/src/`], + tokenRules: TOKEN_RULES, + imports + } +} + +const catalog = build() +writeFileSync(OUT_PATH, JSON.stringify(catalog, null, 2) + '\n', 'utf-8') +const componentCount = Object.values(catalog.imports).filter((e) => e.kind === 'component').length +process.stdout.write( + `catalog.json written: ${Object.keys(catalog.imports).length} exports (${componentCount} components) @ ${catalog.webkitVersion}\n` +) From ee32f8cd01e1ef1dbee98cbbdbb0212f41229fe8 Mon Sep 17 00:00:00 2001 From: HerbertJulio Date: Thu, 2 Jul 2026 10:30:58 -0300 Subject: [PATCH 02/67] [NO-ISSUE] feat(eslint-plugin-webkit): add design-system usage & performance rules --- packages/eslint-plugin-webkit/package.json | 34 +++++ packages/eslint-plugin-webkit/src/catalog.js | 132 ++++++++++++++++++ packages/eslint-plugin-webkit/src/index.js | 69 +++++++++ .../src/rules/no-barrel-import.js | 69 +++++++++ .../src/rules/no-deep-internal-import.js | 57 ++++++++ .../src/rules/no-hardcoded-color.js | 63 +++++++++ .../src/rules/no-whole-icon-set-import.js | 45 ++++++ .../src/rules/prefer-tree-shakeable-root.js | 87 ++++++++++++ .../src/rules/valid-import-path.js | 61 ++++++++ packages/eslint-plugin-webkit/src/util.js | 14 ++ .../test/fixtures/catalog.json | 34 +++++ .../eslint-plugin-webkit/test/rules.test.mjs | 114 +++++++++++++++ 12 files changed, 779 insertions(+) create mode 100644 packages/eslint-plugin-webkit/package.json create mode 100644 packages/eslint-plugin-webkit/src/catalog.js create mode 100644 packages/eslint-plugin-webkit/src/index.js create mode 100644 packages/eslint-plugin-webkit/src/rules/no-barrel-import.js create mode 100644 packages/eslint-plugin-webkit/src/rules/no-deep-internal-import.js create mode 100644 packages/eslint-plugin-webkit/src/rules/no-hardcoded-color.js create mode 100644 packages/eslint-plugin-webkit/src/rules/no-whole-icon-set-import.js create mode 100644 packages/eslint-plugin-webkit/src/rules/prefer-tree-shakeable-root.js create mode 100644 packages/eslint-plugin-webkit/src/rules/valid-import-path.js create mode 100644 packages/eslint-plugin-webkit/src/util.js create mode 100644 packages/eslint-plugin-webkit/test/fixtures/catalog.json create mode 100644 packages/eslint-plugin-webkit/test/rules.test.mjs diff --git a/packages/eslint-plugin-webkit/package.json b/packages/eslint-plugin-webkit/package.json new file mode 100644 index 000000000..6c9e44940 --- /dev/null +++ b/packages/eslint-plugin-webkit/package.json @@ -0,0 +1,34 @@ +{ + "name": "@aziontech/eslint-plugin-webkit", + "version": "0.0.0", + "description": "ESLint rules that force correct and performant usage of the @aziontech/webkit design system. Reads the installed webkit catalog.json (version-locked).", + "license": "MIT", + "type": "module", + "main": "./src/index.js", + "exports": { + ".": "./src/index.js" + }, + "files": [ + "src" + ], + "repository": { + "type": "git", + "url": "https://github.com/aziontech/webkit.git", + "directory": "packages/eslint-plugin-webkit" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "test": "node --test test/*.test.mjs" + }, + "peerDependencies": { + "eslint": ">=8" + }, + "peerDependenciesMeta": { + "vue-eslint-parser": { + "optional": true + } + }, + "keywords": ["eslint", "eslintplugin", "aziontech", "webkit", "design-system"] +} diff --git a/packages/eslint-plugin-webkit/src/catalog.js b/packages/eslint-plugin-webkit/src/catalog.js new file mode 100644 index 000000000..29e22375f --- /dev/null +++ b/packages/eslint-plugin-webkit/src/catalog.js @@ -0,0 +1,132 @@ +// Version-locked catalog loader shared by every rule. +// +// Resolves `@aziontech/webkit/catalog.json` from the LINTED project's node_modules +// (so the allowed-import list / token rules always match the webkit version that +// project installed). Fail-open: if webkit (or its catalog) is not resolvable, the +// catalog reports `available: false` and catalog-dependent rules no-op instead of +// crashing ESLint on unrelated repos. +// +// Test / monorepo-dogfood override: set WEBKIT_CATALOG_PATH to an explicit file. + +import { readFileSync } from 'node:fs' +import { createRequire } from 'node:module' +import { dirname, join } from 'node:path' + +const PKG = '@aziontech/webkit' + +let cache = null // { path, value } + +function resolveCatalogPath(cwd) { + if (process.env.WEBKIT_CATALOG_PATH) return process.env.WEBKIT_CATALOG_PATH + const require = createRequire(join(cwd || process.cwd(), '__webkit_resolve__.js')) + try { + return require.resolve(`${PKG}/catalog.json`) + } catch { + /* fall through */ + } + try { + return join(dirname(require.resolve(`${PKG}/package.json`)), 'catalog.json') + } catch { + return null + } +} + +function levenshtein(a, b) { + const m = a.length + const n = b.length + if (!m) return n + if (!n) return m + const prev = new Array(n + 1) + const curr = new Array(n + 1) + for (let j = 0; j <= n; j++) prev[j] = j + for (let i = 1; i <= m; i++) { + curr[0] = i + for (let j = 1; j <= n; j++) { + const cost = a[i - 1] === b[j - 1] ? 0 : 1 + curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost) + } + for (let j = 0; j <= n; j++) prev[j] = curr[j] + } + return prev[n] +} + +function build(json) { + const imports = json.imports || {} + const subpaths = Object.keys(imports) + const set = new Set(subpaths) + const tokenRules = (json.tokenRules || []).map((r) => ({ + id: r.id, + // Compile WITHOUT the global flag so `.test()` is stateless. + re: new RegExp(r.pattern, (r.flags || '').replace('g', '')), + message: r.message + })) + + return { + available: true, + version: json.webkitVersion || null, + deniedPrefixes: json.deniedPrefixes || [], + tokenRules, + has: (sub) => set.has(sub), + getEntry: (sub) => imports[sub] || null, + /** Nearest published ancestor of a slash path, e.g. `table/x/y` → `table` if published. */ + nearestPublishedPrefix(sub) { + if (!sub.includes('/')) return null + const parts = sub.split('/') + for (let i = parts.length - 1; i > 0; i--) { + const cand = parts.slice(0, i).join('/') + if (set.has(cand)) return cand + } + return null + }, + /** Up to 3 close published subpaths (edit distance ≤ 2), catches typos / singular↔plural. */ + suggestSubpaths(sub) { + const scored = [] + for (const s of subpaths) { + const d = levenshtein(sub, s) + if (d <= 2) scored.push([d, s]) + } + return scored + .sort((a, b) => a[0] - b[0] || a[1].length - b[1].length) + .slice(0, 3) + .map((x) => x[1]) + } + } +} + +function empty() { + return { + available: false, + version: null, + deniedPrefixes: [], + tokenRules: [], + has: () => false, + getEntry: () => null, + nearestPublishedPrefix: () => null, + suggestSubpaths: () => [] + } +} + +export function loadCatalog(cwd) { + const path = resolveCatalogPath(cwd) + if (cache && cache.path === path) return cache.value + let value + if (!path) { + value = empty() + } else { + try { + value = build(JSON.parse(readFileSync(path, 'utf-8'))) + } catch { + value = empty() + } + } + cache = { path, value } + return value +} + +/** Test-only: clear the memoized catalog (RuleTester runs in one process). */ +export function _resetCatalogCache() { + cache = null +} + +export const WEBKIT_PREFIX = `${PKG}/` +export const WEBKIT_BARE = PKG diff --git a/packages/eslint-plugin-webkit/src/index.js b/packages/eslint-plugin-webkit/src/index.js new file mode 100644 index 000000000..73309ea66 --- /dev/null +++ b/packages/eslint-plugin-webkit/src/index.js @@ -0,0 +1,69 @@ +// @aziontech/eslint-plugin-webkit — forces correct + performant usage of the +// @aziontech/webkit design system. Flat-config first (ESLint 9). Namespace: `webkit`. +// +// Presets (flat-config arrays): +// recommended — correctness = error, performance = warn +// strict — everything = error +// performance — only the performance rules + +import validImportPath from './rules/valid-import-path.js' +import noDeepInternalImport from './rules/no-deep-internal-import.js' +import noBarrelImport from './rules/no-barrel-import.js' +import noWholeIconSetImport from './rules/no-whole-icon-set-import.js' +import noHardcodedColor from './rules/no-hardcoded-color.js' +import preferTreeShakeableRoot from './rules/prefer-tree-shakeable-root.js' + +const rules = { + 'valid-import-path': validImportPath, + 'no-deep-internal-import': noDeepInternalImport, + 'no-barrel-import': noBarrelImport, + 'no-whole-icon-set-import': noWholeIconSetImport, + 'no-hardcoded-color': noHardcodedColor, + 'prefer-tree-shakeable-root': preferTreeShakeableRoot +} + +const FILES = ['**/*.vue', '**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.mjs', '**/*.cjs'] + +const plugin = { + meta: { name: '@aziontech/eslint-plugin-webkit', version: '0.0.0' }, + rules +} + +function preset(name, severities) { + return [ + { + name: `webkit/${name}`, + files: FILES, + plugins: { webkit: plugin }, + rules: Object.fromEntries( + Object.entries(severities).map(([rule, sev]) => [`webkit/${rule}`, sev]) + ) + } + ] +} + +const RECOMMENDED = { + 'valid-import-path': 'error', + 'no-deep-internal-import': 'error', + 'no-barrel-import': 'error', + 'no-hardcoded-color': 'warn', + 'prefer-tree-shakeable-root': 'warn', + 'no-whole-icon-set-import': 'warn' +} + +const STRICT = Object.fromEntries(Object.keys(rules).map((r) => [r, 'error'])) + +const PERFORMANCE = { + 'no-barrel-import': 'error', + 'prefer-tree-shakeable-root': 'warn', + 'no-whole-icon-set-import': 'warn' +} + +plugin.configs = { + recommended: preset('recommended', RECOMMENDED), + strict: preset('strict', STRICT), + performance: preset('performance', PERFORMANCE) +} + +export default plugin +export { rules } diff --git a/packages/eslint-plugin-webkit/src/rules/no-barrel-import.js b/packages/eslint-plugin-webkit/src/rules/no-barrel-import.js new file mode 100644 index 000000000..6469b285c --- /dev/null +++ b/packages/eslint-plugin-webkit/src/rules/no-barrel-import.js @@ -0,0 +1,69 @@ +// Blocks a barrel import from the bare package — `import { Button } from '@aziontech/webkit'` +// — which defeats tree-shaking (and today does not even resolve, since webkit publishes +// no `.` export). Import each component from its own subpath instead. Type-only imports +// are exempt (erased at build). Namespace imports are flagged separately. + +import { WEBKIT_BARE, WEBKIT_PREFIX } from '../catalog.js' + +function kebab(name) { + return name + .replace(/([a-z0-9])([A-Z])/g, '$1-$2') + .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2') + .toLowerCase() +} + +export default { + meta: { + type: 'problem', + docs: { description: 'Disallow barrel imports from the bare @aziontech/webkit entry (defeats tree-shaking).' }, + hasSuggestions: true, + schema: [], + messages: { + barrel: + 'Barrel import from @aziontech/webkit defeats tree-shaking. Import each component from its own subpath (e.g. {{example}}).', + namespace: + 'Namespace import of @aziontech/webkit pulls in every component. Import only the subpaths you use.', + split: 'Split into per-subpath default imports' + } + }, + create(context) { + return { + ImportDeclaration(node) { + if (node.source.value !== WEBKIT_BARE) return + if (node.importKind === 'type') return + + const named = node.specifiers.filter( + (s) => s.type === 'ImportSpecifier' && s.importKind !== 'type' + ) + const namespace = node.specifiers.find((s) => s.type === 'ImportNamespaceSpecifier') + + if (namespace) { + context.report({ node, messageId: 'namespace' }) + return + } + if (!named.length) return + + const first = named[0] + const example = `import ${first.local.name} from '${WEBKIT_PREFIX}${kebab(first.imported.name)}'` + context.report({ + node, + messageId: 'barrel', + data: { example }, + suggest: [ + { + messageId: 'split', + fix: (fixer) => { + const lines = named.map((s) => { + const local = s.local.name + const sub = kebab(s.imported.name) + return `import ${local} from '${WEBKIT_PREFIX}${sub}'` + }) + return fixer.replaceText(node, lines.join('\n')) + } + } + ] + }) + } + } + } +} diff --git a/packages/eslint-plugin-webkit/src/rules/no-deep-internal-import.js b/packages/eslint-plugin-webkit/src/rules/no-deep-internal-import.js new file mode 100644 index 000000000..27161d668 --- /dev/null +++ b/packages/eslint-plugin-webkit/src/rules/no-deep-internal-import.js @@ -0,0 +1,57 @@ +// Blocks reaching into webkit internals: `@aziontech/webkit/src/**` or any subpath +// deeper than a published entry point (e.g. `.../table/internal/x`). These break on +// any refactor and bypass the public API. Suggests the published entry point. + +import { loadCatalog, WEBKIT_PREFIX } from '../catalog.js' +import { ctxCwd } from '../util.js' + +export default { + meta: { + type: 'problem', + docs: { description: 'Disallow importing @aziontech/webkit internals (deep paths / src).' }, + hasSuggestions: true, + schema: [], + messages: { + deep: "'{{source}}' reaches into @aziontech/webkit internals. Import the published entry point{{hint}}.", + replace: "Import from '{{replacement}}'" + } + }, + create(context) { + const catalog = loadCatalog(ctxCwd(context)) + if (!catalog.available) return {} + + function check(sourceNode) { + if (!sourceNode || typeof sourceNode.value !== 'string') return + const source = sourceNode.value + if (!source.startsWith(WEBKIT_PREFIX)) return + const sub = source.slice(WEBKIT_PREFIX.length) + if (catalog.has(sub)) return + const isSrc = sub.startsWith('src/') + const prefix = catalog.nearestPublishedPrefix(sub) + if (!isSrc && !prefix) return // unknown-but-not-deep → valid-import-path owns it + + const replacement = prefix ? WEBKIT_PREFIX + prefix : null + context.report({ + node: sourceNode, + messageId: 'deep', + data: { source, hint: replacement ? ` '${replacement}'` : '' }, + suggest: replacement + ? [ + { + messageId: 'replace', + data: { replacement }, + fix: (fixer) => fixer.replaceText(sourceNode, JSON.stringify(replacement)) + } + ] + : [] + }) + } + + return { + ImportDeclaration: (node) => check(node.source), + ExportNamedDeclaration: (node) => node.source && check(node.source), + ExportAllDeclaration: (node) => node.source && check(node.source), + ImportExpression: (node) => node.source?.type === 'Literal' && check(node.source) + } + } +} diff --git a/packages/eslint-plugin-webkit/src/rules/no-hardcoded-color.js b/packages/eslint-plugin-webkit/src/rules/no-hardcoded-color.js new file mode 100644 index 000000000..ea1eea92e --- /dev/null +++ b/packages/eslint-plugin-webkit/src/rules/no-hardcoded-color.js @@ -0,0 +1,63 @@ +// Flags hardcoded colors / raw palette / raw typography in class & style strings, in +// both ", filename: 'a.vue' }, + // already tree-shakeable root + { code: "", filename: 'b.vue' }, + // non-compound component + { code: "", filename: 'c.vue' } + ], + invalid: [ + { + code: "", + filename: 'd.vue', + errors: [{ messageId: 'preferRoot', suggestions: 1 }] + } + ] + }) +}) From dacd666ba0988f290ec9563b5e50195c7375157c Mon Sep 17 00:00:00 2001 From: HerbertJulio Date: Thu, 2 Jul 2026 10:30:58 -0300 Subject: [PATCH 03/67] [NO-ISSUE] feat(stylelint-config-webkit): add token-discipline config for styles --- packages/stylelint-config-webkit/.releaserc | 74 +++++++++++++++++++ packages/stylelint-config-webkit/README.md | 71 ++++++++++++++++++ packages/stylelint-config-webkit/index.js | 45 +++++++++++ packages/stylelint-config-webkit/package.json | 36 +++++++++ .../test/config.test.mjs | 56 ++++++++++++++ 5 files changed, 282 insertions(+) create mode 100644 packages/stylelint-config-webkit/.releaserc create mode 100644 packages/stylelint-config-webkit/README.md create mode 100644 packages/stylelint-config-webkit/index.js create mode 100644 packages/stylelint-config-webkit/package.json create mode 100644 packages/stylelint-config-webkit/test/config.test.mjs diff --git a/packages/stylelint-config-webkit/.releaserc b/packages/stylelint-config-webkit/.releaserc new file mode 100644 index 000000000..430652c8a --- /dev/null +++ b/packages/stylelint-config-webkit/.releaserc @@ -0,0 +1,74 @@ +{ + "branches": ["dev"], + "tagFormat": "@aziontech/stylelint-config-webkit@${version}", + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "presetOpts": { + "gitRawCommitsOpts": { + "path": "packages/stylelint-config-webkit" + } + }, + "parserOpts": { + "headerPattern": "^(\\[[\\w-]+\\]\\s+)?(\\w+)(?:\\(([\\w-]+)\\))?!?:\\s(.*)$", + "headerCorrespondence": ["ticket", "type", "scope", "subject"] + }, + "releaseRules": [ + { "type": "feat", "release": "minor" }, + { "type": "fix", "release": "patch" }, + { "type": "hotfix", "release": "patch" }, + { "type": "chore", "release": "patch" }, + { "type": "docs", "release": "patch" }, + { "type": "style", "release": "patch" }, + { "type": "refactor", "release": "patch" }, + { "type": "perf", "release": "patch" }, + { "type": "test", "release": false }, + { "type": "ci", "release": false }, + { "type": "revert", "release": false }, + { "breaking": true, "release": "major" } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "parserOpts": { + "headerPattern": "^(\\[[\\w-]+\\]\\s+)?(\\w+)(?:\\(([\\w-]+)\\))?!?:\\s(.*)$", + "headerCorrespondence": ["ticket", "type", "scope", "subject"] + } + } + ], + "@semantic-release/changelog", + [ + "@semantic-release/npm", + { + "npmPublish": false + } + ], + [ + "@semantic-release/exec", + { + "publishCmd": "npm publish --provenance --access public" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "package.json", + "CHANGELOG.md" + ], + "message": "chore(release): @aziontech/stylelint-config-webkit ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", + { + "labels": false + } + ] + ] +} diff --git a/packages/stylelint-config-webkit/README.md b/packages/stylelint-config-webkit/README.md new file mode 100644 index 000000000..533984ff1 --- /dev/null +++ b/packages/stylelint-config-webkit/README.md @@ -0,0 +1,71 @@ +# @aziontech/stylelint-config-webkit + +Shareable [Stylelint](https://stylelint.io/) config that forbids hardcoded colors in CSS/SCSS and Vue ` +``` diff --git a/packages/webkit/cli-templates/claude/rules/webkit-testid.md b/packages/webkit/cli-templates/claude/rules/webkit-testid.md new file mode 100644 index 000000000..50725c7ac --- /dev/null +++ b/packages/webkit/cli-templates/claude/rules/webkit-testid.md @@ -0,0 +1,80 @@ +# Rule: `data-testid` — derived name on the root, overridable + +Every webkit component root carries a **`data-testid`** derived from the component's own +name — **`-`**, or **`input-`** for input components (`input-chip`, +`content-badge`, `feedback-toast`, `data-table`). Your tests target that attribute instead +of inventing selectors, and you override it per instance by passing `data-testid` on the +tag. Components you author in this project follow the same pattern. This is a convention +enforced by review — no lint rule checks it, so apply it deliberately. + +## Do + +- Target components in tests via their derived `data-testid`, never via DOM structure or + internal classes. +- Override the testid per instance when a page renders more than one of the same + component: `